JavaScript:用于对象的 filter()

IT技术 javascript jquery object filtering
2021-01-19 11:50:36

如果我理解正确的话ECMAScript 5 有类型filter()原型Array,但没有Object类型。

我将如何在 JavaScript 中实现filter()for Objects?

假设我有这个对象:

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 的站点时,我在 FireBug 中收到 JavaScript 错误。

6个回答

首先,扩展Object.prototype. 相反,将您的功能作为独立功能提供,或者如果您真的想扩展一个全局功能,请将其作为实用程序提供在 上Object,就像已经有Object.keys, Object.assign, Object.is, ... 等。

我在这里提供了几种解决方案:

  1. 使用reduceObject.keys
  2. 如(1),结合 Object.assign
  3. 使用map和传播语法而不是reduce
  4. 使用Object.entriesObject.fromEntries

1. 使用reduceObject.keys

使用reduceObject.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工作原理。

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.entriesObject.fromEntries

由于解决方案将对象转换为中间数组,然后将其转换回普通对象,因此使用Object.entries(ES2017) 和相反的(即从键/值对数组创建对象)和Object.fromEntries( ES2019)。

它导致了这种“单行”方法Object

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);

谓词函数在这里获取一个键/值对作为参数,这有点不同,但允许谓词函数的逻辑中有更多的可能性。

@IamStalker,你试过了吗?没关系,只要您在第二个参数中提供有效的函数即可。注意:我不知道最后.Filter什么,但如果它是一个函数,你需要调用它 ( x => x.Expression.Filters.Filter())
2021-03-13 11:50:36
最后一个变体的 TypeScript 版本:gist.github.com/OliverJAsh/acafba4f099f6e677dbb0a38c60dc33d
2021-03-19 11:50:36
我毫不怀疑这是最好的解决方案。恭喜。我最喜欢的解决方案是与entries的组合fromEntries如此简单,却又如此易读、易懂、功能强大且具有更高层次的抽象!
2021-03-20 11:50:36
很好!感谢您提供这些出色的解释和示例。
2021-03-22 11:50:36
较新的功能可能会减少代码,但也会降低性能Ed 3 兼容代码在大多数浏览器中的运行速度是其两倍以上。
2021-04-01 11:50:36

永远不要扩展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) {
    let result = {}, key;

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

    return result;
};
@patrick dw:不。首先,我没有提到扩展/不扩展原型。其次,developer.mozilla.org /en/JavaScript/Reference/Global_Objects/…——“创建一个新数组,其中包含所有通过所提供函数实现的测试的元素。” 在全局上实现完全相反的方法似乎很愚蠢,不是吗?
2021-03-11 11:50:36
@pyrotechnick:不。首先,答案的要点是不要扩展Object.prototype,而只是将函数放在Object. 其次,这是OP的代码显然OP的意图是让.filter()是这样的,它过滤掉了积极的成果。换句话说,它是一个负过滤器,其中正返回值意味着它被排除在结果之外。如果您查看 jsFiddle 示例,他会过滤掉undefined.
2021-03-15 11:50:36
你做错了... !predicate(obj[key]) 应该是 predicate(obj[key])
2021-03-29 11:50:36
@pyrotechnick:没错,你没有提到扩展/不扩展原型,这就是我的观点。你说我做错了,但我做的唯一“它”就是告诉 OP 不要扩展Object.prototype. 从问题:“这有效......,但是当我将它添加到我的网站......时,我会收到 JavaScript 错误”如果 OP 决定实现.filter()与 的行为相反的行为Array.prototpe.filter,那取决于他/她。如果您想通知 OP 代码错误,请在问题下发表评论,但不要在不是我的代码时告诉我做错了。
2021-03-31 11:50:36
@patrick:给一个人一个面包,你会喂他一天,教他如何烘焙,你会喂他一辈子(或者什么,我是丹麦人,我不知道正确的英语谚语;)
2021-04-04 11:50:36

从 2020 年开始在 Vanilla JS 中的解决方案。


let romNumbers={'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}

您可以romNumbers按键过滤对象:

const filteredByKey = Object.fromEntries(
    Object.entries(romNumbers).filter(([key, value]) => key === 'I') )
// filteredByKey = {I: 1} 

romNumbers按值过滤对象:

 const filteredByValue = Object.fromEntries(
    Object.entries(romNumbers).filter(([key, value]) => value === 5) )
 // filteredByValue = {V: 5} 
很棒的解决方案!将对象分解为entries,按所需条件过滤,然后使用fromEntries.
2021-03-17 11:50:36
我使用该解决方案通过多个键过滤对象includesObject.fromEntries(Object.entries(romNumbers).filter(([key, value]) => ['a', 'b', 'c', 'd', 'e'].includes(key)))
2021-04-07 11:50:36
不知道你为什么要重复一年多前给出的答案
2021-04-08 11:50:36

如果您愿意使用下划线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}
我只是为此使用了 lodash,这是一个很好的解决方案。谢谢@Bogdan D!
2021-03-15 11:50:36
lodash 是一个糟糕的解决方案,因为过滤空对象也会删除数字。
2021-03-22 11:50:36
@mibbit 你能更具体点吗?我相信这只是正确实现回调的问题
2021-04-06 11:50:36

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"
  }
}
看起来挺好的!但是为什么它只返回另一个对象(而不是名称/filterValue 为“Alireza”的对象)?
2021-03-24 11:50:36
@Pille,OP 要求它像那样(注意!predicate他们自己代码中的否定过滤器)。
2021-04-06 11:50:36