JavaScript:对象的filter()

Agi*_*ble 136 javascript jquery filtering object

如果我理解正确,ECMAScript 5有类型的filter()原型Array,但不是Object类型.

我如何在JavaScript中实现filter()for Object

假设我有这个对象:

var foo = {
    bar: "Yes"
};
Run Code Online (Sandbox Code Playgroud)

我想写一个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;
};
Run Code Online (Sandbox Code Playgroud)

这在我在下面的演示中使用时有效,但是当我将它添加到使用jQuery 1.5和jQuery UI 1.8.9的网站时,我在FireBug中遇到了JavaScript错误.

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);
Run Code Online (Sandbox Code Playgroud)
#disp {
  white-space: pre;
  font-family: monospace
}
Run Code Online (Sandbox Code Playgroud)
<div id="disp"></div>
Run Code Online (Sandbox Code Playgroud)

tri*_*cot 213

首先,扩展被认为是不好的做法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);
Run Code Online (Sandbox Code Playgroud)

注意,在上面的代码中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);
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

4.使用Object.entriesObject.fromEntries

当解决方案将对象转换为中间数组然后将其转换回普通对象时,使用Object.entries(ES2017)并使用相反方法(即从关键数组创建对象)将非常有用/值对).在撰写本文时,Object.fromEntries提案已进入第3阶段,Firefox已实施提案.否则可以使用填充物.

它导致了这两种"单线"方法Object(包括polyfill):

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);
Run Code Online (Sandbox Code Playgroud)

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

  • @IamStalker,您尝试过吗?没关系,只要您在第二个参数中提供有效的函数即可。注意:我不知道`.Filter`到底是什么,但是如果它是一个函数,则需要调用它(`x =&gt; x.Expression.Filters.Filter()`) (2认同)
  • 最后一个变体的 TypeScript 版本:https://gist.github.com/OliverJAsh/acafba4f099f6e677dbb0a38c60dc33d (2认同)
  • 我毫不怀疑这是最好的解决方案。恭喜。我最喜欢的解决方案是与“entries”和“fromEntries”的组合。如此简单,但又如此可读、可理解、强大且更高层次的抽象! (2认同)
  • @CiaranGallagher,当我在示例对象中添加 `other: null` 作为额外属性时,代码片段 #3 仍然运行良好。我想您的过滤条件正在检查每个值的属性,只有当您的值不为“null”时,这才有意义。如果您的情况如此,您可以在过滤条件中使用“??”运算符。但这与这里的答案关系不大,而更多的是提供可靠的过滤条件。 (2认同)

use*_*716 167

永远不要延伸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!"
Run Code Online (Sandbox Code Playgroud)

而是创建一个传递对象的函数.

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;
};
Run Code Online (Sandbox Code Playgroud)

  • @patrick:给一个男人一个面包,你会喂他一天,教他如何烤,你会喂他一辈子(或者别的,我是丹麦人,我不知道正确的英语说法;) (43认同)
  • 你做错了......!谓词(obj [key])应该是谓词(obj [key]) (13认同)
  • @pyrotechnick:没错,你没有提到扩展/不扩展原型,这就是我的观点.你说我做错了,但我正在做的唯一"它"是告诉OP不要扩展`Object.prototype`.从问题:**"这有用...,但当我将它添加到我的网站...时,我得到JavaScript错误"**如果OP决定实现一个`.filter()`与其相反的行为'Array.prototpe.filter`,这取决于他/她.如果你想通知OP代码是错误的,请在问题下留言,但是如果不是我的代码,请不要告诉我*我做错了. (6认同)
  • @pyrotechnick:否。首先,答案的重点是不扩展Object.prototype,而只是将函数放在Object上。其次,*这是OP的代码*。显然,OP的意图是使.filter()能够“过滤”出积极的结果。换句话说,它是一个负过滤器,其中正返回值意味着它被“排除”在结果中。如果看jsFiddle示例,他正在过滤掉那些未定义的现有属性。 (4认同)
  • @patrick dw:不.首先,我没有提到扩展/不扩展原型.其次,https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter - "创建一个新数组,其中包含通过所提供函数实现的测试的所有元素." 在全局上实现完全相反的看起来很傻,不是吗? (4认同)
  • @BenCarp对象带有用户未定义的其他属性(内置属性),因此可以避免这些属性 (3认同)

Qui*_*inn 88

从 2020 年开始使用 Vanilla JS 的解决方案。


let romNumbers={'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
Run Code Online (Sandbox Code Playgroud)

您可以romNumbers按键过滤对象:

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

romNumbers按值过滤对象:

 const filteredByValue = Object.fromEntries(
    Object.entries(romNumbers).filter(([key, value]) => value === 5) )
 // filteredByValue = {V: 5} 
Run Code Online (Sandbox Code Playgroud)

  • 很棒的解决方案!将对象分解为“entries”,按所需条件进行过滤,然后使用“fromEntries”创建一个新对象。 (3认同)
  • 不知道为什么你重复一年多前给出的答案(/sf/answers/2633127311/) (3认同)
  • 我使用该解决方案通过“includes”按多个键过滤对象。`Object.fromEntries(Object.entries(romNumbers).filter(([key, value]) =&gt; ['a', 'b', 'c', 'd', 'e'].includes(key)) )` (2认同)

Bog*_*n D 19

如果你愿意使用下划线lodash,你可以使用pick(或相反omit).

下划线文档中的示例:

_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
// {name: 'moe', age: 50}
Run Code Online (Sandbox Code Playgroud)

或者使用回调(对于lodash,使用pickBy):

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


fra*_*cis 6

普通 ES6:

var foo = {
    bar: "Yes"
};

const res = Object.keys(foo).filter(i => foo[i] === 'Yes')

console.log(res)
// ["bar"]
Run Code Online (Sandbox Code Playgroud)


Mar*_*sen 5

正如patrick所说的,这是一个坏主意,因为它几乎肯定会破坏您可能希望使用的任何第三方代码。

如果扩展Object.prototype,所有类似jquery或prototype的库都将中断,原因是对象上的惰性迭代(不带hasOwnProperty检查)将中断,因为您添加的功能将成为迭代的一部分。


Mr.*_*irl 5

我创建了一个Object.filter()不仅按函数过滤,而且还接受要包含的键数组。可选的第三个参数将允许您反转过滤器。

鉴于:

var foo = {
    x: 1,
    y: 0,
    z: -1,
    a: 'Hello',
    b: 'World'
}
Run Code Online (Sandbox Code Playgroud)

大批:

Object.filter(foo, ['z', 'a', 'b'], true);
Run Code Online (Sandbox Code Playgroud)

功能:

Object.filter(foo, function (key, value) {
    return Ext.isString(value);
});
Run Code Online (Sandbox Code Playgroud)

代码

免责声明:为了简洁起见,我选择使用 Ext JS 核心。认为没有必要为对象类型编写类型检查器,因为它不是问题的一部分。

var foo = {
    x: 1,
    y: 0,
    z: -1,
    a: 'Hello',
    b: 'World'
}
Run Code Online (Sandbox Code Playgroud)
Object.filter(foo, ['z', 'a', 'b'], true);
Run Code Online (Sandbox Code Playgroud)
Object.filter(foo, function (key, value) {
    return Ext.isString(value);
});
Run Code Online (Sandbox Code Playgroud)


Abd*_*UMI 5

给定的

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

keys = ['firstname', 'age'];
Run Code Online (Sandbox Code Playgroud)

然后 :

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

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

keys = ['firstname', 'age'];
Run Code Online (Sandbox Code Playgroud)

  • 这不使用问题所需的给定谓词函数。 (3认同)

sha*_*unw 5

怎么样:

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

或者...

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


Ali*_*eza 5

ES6方法...

假设您在下面有这个对象:

const developers = {
  1: {
   id: 1,
   name: "Brendan", 
   family: "Eich"
  },
  2: {
   id: 2,
   name: "John", 
   family: "Resig"
  },  
  3: {
   id: 3,
   name: "Alireza", 
   family: "Dezfoolian"
 }
};
Run Code Online (Sandbox Code Playgroud)

创建一个函数:

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

并称之为:

filterObject(developers, "name", "Alireza");
Run Code Online (Sandbox Code Playgroud)

返回

{
  1: {
  id: 1,
  name: "Brendan", 
  family: "Eich"
  },
  2: {
   id: 2,
   name: "John", 
   family: "Resig"
  }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

    var foo = {
    bar: "Yes",
    pipe: "No"
};

const ret =  Object.entries(foo).filter(([key, value])=> value === 'Yes');
Run Code Online (Sandbox Code Playgroud)

https://masteringjs.io/tutorials/fundamentals/filter-object