JavaScript: filter() for Objects

2022-08-29 23:52:57

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

如何在 JavaScript 中实现 a for s?filter()Object

假设我有这个对象:

var foo = {
    bar: "Yes"
};

我想写一个适用于s的:filter()Object

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错误。

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>

答案 1

首先,扩展Object.prototype被认为是不好的做法。相反,将您的功能作为独立功能提供,或者如果您真的想扩展全局,请将其作为实用程序功能提供,就像已经存在 ,, , , ...等。ObjectObject.keysObject.assignObject.is

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

  1. 使用和reduceObject.keys
  2. 如 (1),结合Object.assign
  3. 使用和传播语法而不是mapreduce
  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);

请注意,在上面的代码中必须是包含条件(与OP使用的排除条件相反),以便它符合Array.prototype.filter的工作方式。predicate

2. 作为(1),结合Object.assign

在上面的解决方案中,在部件中使用逗号运算符来返回突变的对象。这当然可以写成两个语句而不是一个表达式,但后者更简洁。要在不使用逗号运算符的情况下执行此操作,您可以使用 Object.assign,这将返回突变的对象:reduceres

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. 使用和扩展语法而不是mapreduce

在这里,我们将调用移出循环,因此它只进行一次,并将其作为单独的参数传递给各个键(使用扩展语法):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);

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


答案 2

永远不要延长.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;
};