JavaScript: filter() для объектов


ECMAScript 5 имеет filter() прототип Array типы, но не Object типы, если я правильно понял.

как бы я реализовал filter() на Objects в JavaScript?

скажем, у меня есть этот объект:

var foo = {
    bar: "Yes"
};

и я хочу написать filter() что работает на Objects:

Object.prototype.filter = function(predicate) {
    var result = {};

    for (key in this) {
        if (this.hasOwnProperty(key) && !predicate(this[key])) {
            result[key] = this[key];
        }
    }

    return result;
};

это работает, когда я использую его в следующей демонстрации, но когда я добавляю его на свой сайт, который использует jQuery 1.5 и jQuery UI 1.8.9, я получаю Ошибки JavaScript в FireBug.

Object.prototype.filter = function(predicate) {
  var result = {};
  for (key in this) {
    if (this.hasOwnProperty(key) && !predicate(this[key])) {
      console.log("copying");
      result[key] = this[key];
    }
  }
  return result;
};

var foo = {
  bar: "Yes",
  moo: undefined
};

foo = foo.filter(function(property) {
  return typeof property === "undefined";
});

document.getElementById('disp').innerHTML = JSON.stringify(foo, undefined, '  ');
console.log(foo);
#disp {
  white-space: pre;
  font-family: monospace
}
<div id="disp"></div>
8 77

8 ответов:

никогда и нигде не распространяется Object.prototype.

ужасные вещи будут происходить с вашим кодом. Все сломается. Вы расширяетесь все типы объектов, включая литералы объектов.

вот быстрый пример, который вы можете попробовать:

    // Extend Object.prototype
Object.prototype.extended = "I'm everywhere!";

    // See the result
alert( {}.extended );          // "I'm everywhere!"
alert( [].extended );          // "I'm everywhere!"
alert( new Date().extended );  // "I'm everywhere!"
alert( 3..extended );          // "I'm everywhere!"
alert( true.extended );        // "I'm everywhere!"
alert( "here?".extended );     // "I'm everywhere!"

вместо этого создайте функцию, которую вы передаете объекту.

Object.filter = function( obj, predicate) {
    var result = {}, key;
    // ---------------^---- as noted by @CMS, 
    //      always declare variables with the "var" keyword

    for (key in obj) {
        if (obj.hasOwnProperty(key) && !predicate(obj[key])) {
            result[key] = obj[key];
        }
    }

    return result;
};

прежде всего,считается плохой практикой расширять Object.prototype. Вместо этого предоставьте свою функцию в качестве функции полезности на Object, так же, как уже есть Object.keys,Object.assign,Object.is, ...так далее.

можно использовать reduce и Object.keys для реализации нужного фильтра (с помощью ES6 синтаксис стрелой):

Object.filter = (obj, predicate) => 
    Object.keys(obj)
          .filter( key => predicate(obj[key]) )
          .reduce( (res, key) => (res[key] = obj[key], res), {} );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

обратите внимание, что в приведенном выше коде predicate должен быть включение состояние (вопреки исключение условие OP используется), так что он находится в соответствии с тем, как Array.prototype.filter строительство.

С Object.assign

в приведенном выше решении оператор запятая используется reduce часть, чтобы вернуть мутировавший

если вы используете подчеркивание или лодашь, вы можете использовать pick (или, напротив, omit).

примеры из документов подчеркивания:

_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
// {name: 'moe', age: 50}

или с обратным вызовом (для лодашь используйте pickBy):

_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
  return _.isNumber(value);
});
// {age: 50}

как Патрик уже заявил, Это плохая идея, так как она почти наверняка сломает любой сторонний код, который вы когда-либо хотели бы использовать.

все библиотеки, такие как jquery или prototype, сломаются, если вы расширите Object.prototype, причина в том, что ленивая итерация над объектами (без hasOwnProperty проверки) сломается, так как функции, которые вы добавляете, будут частью итерации.

Я создал Object.filter() который не только фильтрует по функции, но и принимает массив ключей для включения. Дополнительный третий параметр позволит вам инвертировать фильтр.

дано:

var foo = {
    x: 1,
    y: 0,
    z: -1,
    a: 'Hello',
    b: 'World'
}

время:

Object.filter(foo, ['z', 'a', 'b'], true);

функция:

Object.filter(foo, function (key, value) {
    return Ext.isString(value);
});

код

отказ от ответственности: я решил использовать Ext JS core для краткости. Не посчитал нужным писать шашки типов для типов объектов как это не было частью вопроса.

// Helper functions
function clone(obj) { return JSON.parse(JSON.stringify(obj)); }
function print(obj) {
    document.getElementById('disp').innerHTML += JSON.stringify(obj, undefined, '  ') + '<br />';
    console.log(obj);
}

Object.filter = function (obj, ignore, invert) {
    if (ignore === undefined) {
        return obj;   
    }
    invert = invert || false;
    var not = function(condition, yes) { return yes ? !condition : condition; };
    var isArray = Ext.isArray(ignore);
    for (var key in obj) {
        if (obj.hasOwnProperty(key) &&
                (isArray && not(!Ext.Array.contains(ignore, key), invert)) ||
                (!isArray && not(!ignore.call(undefined, key, obj[key]), invert))) {
            delete obj[key];
        }
    }
    return obj;
};

var foo = {
    x: 1,
    y: 0,
    z: -1,
    a: 'Hello',
    b: 'World'
}, bar = clone(foo);

print(Object.filter(foo, ['z', 'a', 'b'], true));
print(Object.filter(bar, function (key, value) {
    return Ext.isString(value);
}));
#disp {
    white-space: pre;
    font-family: monospace
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/extjs/4.2.1/builds/ext-core.min.js"></script>
<div id="disp"></div>

дано

object = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};

keys = ['firstname', 'age'];

затем :

keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});
// {firstname:'abd', age: 16}

// Helper
function filter(object, ...keys) {
  return keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});
  
};

//Example
const person = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};

// Expected to pick only firstname and age keys
console.log(
  filter(person, 'firstname', 'age')
)

Как насчет:

function filterObj(keys, obj) {
  const newObj = {};
  for (let key in obj) {
    if (keys.includes(key)) {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

или...

function filterObj(keys, obj) {
  const newObj = {};
  Object.keys(obj).forEach(key => {
    if (keys.includes(key)) {
      newObj[key] = obj[key];
    }
  });
  return newObj;
}

в этих случаях я использую jquery $.карта, которая может обрабатывать объекты. Как уже упоминалось в других ответах, не рекомендуется менять собственные прототипы (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes)

Ниже приведен пример фильтрации, просто проверяя некоторые свойства вашего объекта. Он возвращает собственный объект, если ваше условие истинно или возвращает undefined если не. Этот undefined свойство сделает эту запись исчезнет из списка объектов;

$.map(yourObject, (el, index)=>{
    return el.yourProperty ? el : undefined;
});