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

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

Как мне реализовать filter() для Object в JavaScript?

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

var foo = {
    bar: "Yes"
};

И я хочу написать filter(), который работает на Object s:

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>
+114
21 февр. '11 в 22:42
источник поделиться
11 ответов

Никогда не расширяйте 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;
};
+153
21 февр. '11 в 22:43
источник

Связанные вопросы


Похожие вопросы

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

Я приведу здесь несколько решений:

  1. Использование reduce и Object.keys
  2. Как (1), в сочетании с Object.assign
  3. Использование map и распространение синтаксиса вместо reduce
  4. Использование Object.entries и Object.fromEntries

1. С помощью reduce и Object.keys

С 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 работы Array.prototype.filter.

2. Как (1), в сочетании с Object.assign

В вышеприведенном решении оператор запятой используется в части reduce чтобы вернуть мутированный объект res. Конечно, это можно записать в виде двух утверждений вместо одного выражения, но последнее является более кратким. Чтобы сделать это без оператора запятой, вы можете вместо этого использовать Object.assign, который возвращает мутированный объект:

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

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

3. Использование map и распространения синтаксиса вместо reduce

Здесь мы перемещаем вызов Object.assign из цикла, чтобы он был выполнен только один раз, и передаем ему отдельные ключи в качестве отдельных аргументов (используя синтаксис расширения):

Object.filter = (obj, predicate) => 
    Object.assign(...Object.keys(obj)
                    .filter( key => predicate(obj[key]) )
                    .map( key => ({ [key]: obj[key] }) ) );

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

4. Использование Object.entries и Object.fromEntries

Поскольку решение переводит объект в промежуточный массив, а затем преобразует его обратно в простой объект, было бы полезно использовать Object.entries (ES2017) и использовать метод, который делает противоположное (то есть создает объект из массив пар ключ/значение). На момент написания Object.fromEntries предложение Object.fromEntries на этапе 3. Firefox и, в последнее время, Chrome реализовали его. В противном случае можно использовать полифилл.

Это приводит к этим двум "однострочным" методам на Object (включая polyfill):

Object.fromEntries = arr => Object.assign({}, ...arr.map( ([k, v]) => ({[k]: v}) ));
Object.filter = (obj, predicate) => 
                  Object.fromEntries(Object.entries(obj).filter(predicate));

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};

var filtered = Object.filter(scores, ([name, score]) => score > 1); 
console.log(filtered);

Функция предиката получает здесь пару ключ/значение в качестве аргумента, которая немного отличается, но допускает больше возможностей в логике функции предиката.

+184
03 июн. '16 в 13:46
источник

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

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

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

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

_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
  return _.isNumber(value);
});
// {age: 50}
+18
05 окт. '16 в 11:40
источник

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

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

+5
21 февр. '11 в 22:47
источник

ES6 подход...

Представьте, что у вас есть этот объект ниже:

const developers = {
  1: {
   id: 1,
   name: "Brendan", 
   family: "Eich"
  },
  2: {
   id: 2,
   name: "John", 
   family: "Resig"
  },  
  3: {
   id: 3,
   name: "Alireza", 
   family: "Dezfoolian"
 }
};

Создать функцию:

const filterObject = (obj, filter, filterValue) => 
   Object.keys(obj).reduce((acc, val) => 
   (obj[val][filter] === filterValue ? acc : {
       ...acc,
       [val]: obj[val]
   }                                        
), {});

И назовите это:

filterObject(developers, "name", "Alireza");

и вернется:

{
  1: {
  id: 1,
  name: "Brendan", 
  family: "Eich"
  },
  2: {
   id: 2,
   name: "John", 
   family: "Resig"
  }
}
+3
04 февр. '19 в 7:21
источник

Я создал 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 для краткости. Не чувствовал необходимости писать контролеры типов для типов объектов, так как это не было частью вопроса.

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

Object.filter = function (obj, ignore, invert) {
    let result = {}; // Returns a filtered copy of the original list
    if (ignore === undefined) {
        return obj;   
    }
    invert = invert || false;
    let not = function(condition, yes) { return yes ? !condition : condition; };
    let 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))) {
            result[key] = obj[key];
        }
    }
    return result;
};

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

print(Object.filter(foo, ['z', 'a', 'b'], true));
print(Object.filter(foo, (key, value) => 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>
+3
31 дек. '15 в 17:31
источник

Дано

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')
)
+2
12 нояб. '16 в 11:17
источник

Как насчет:

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;
}
+1
20 мая '17 в 17:33
источник

Мое самоуверенное решение:

function objFilter(obj, filter, nonstrict){
  r = {}
  if (!filter) return {}
  if (typeof filter == 'string') return {[filter]: obj[filter]}
  for (p in obj) {
    if (typeof filter == 'object' &&  nonstrict && obj[p] ==  filter[p]) r[p] = obj[p]
    else if (typeof filter == 'object' && !nonstrict && obj[p] === filter[p]) r[p] = obj[p]
    else if (typeof filter == 'function'){ if (filter(obj[p],p,obj)) r[p] = obj[p]}
    else if (filter.length && filter.includes(p)) r[p] = obj[p]
  }
  return r
}

Тестовые случаи:

obj = {a:1, b:2, c:3}

objFilter(obj, 'a') // returns: {a: 1}
objFilter(obj, ['a','b']) // returns: {a: 1, b: 2}
objFilter(obj, {a:1}) // returns: {a: 1}
objFilter(obj, {'a':'1'}, true) // returns: {a: 1}
objFilter(obj, (v,k,o) => v%2===1) // returns: {a: 1, c: 3}

https://gist.github.com/khullah/872d5a174108823159d845cc5baba337

0
13 дек. '18 в 18:22
источник

Как все говорили, не облажайтесь с прототипом. Вместо этого просто напишите функцию для этого. Вот моя версия с lodash:

import each from 'lodash/each';
import get from 'lodash/get';

const myFilteredResults = results => {
  const filteredResults = [];

  each(results, obj => {
    // filter by whatever logic you want.

    // sample example
    const someBoolean = get(obj, 'some_boolean', '');

    if (someBoolean) {
      filteredResults.push(obj);
    }
  });

  return filteredResults;
};
0
24 июн. '19 в 5:07
источник

В этих случаях я использую jquery $.map, который может обрабатывать объекты. Как упоминалось в других ответах, не рекомендуется менять собственные прототипы (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;
});
-1
04 сент. '18 в 7:47
источник

Посмотрите другие вопросы по меткам или Задайте вопрос