JavaScript:对象的filter()
如果我理解正确的话,ECMAScript 5 有 Array
类型的 filter()
原型,但没有 Object
类型。
如何在 JavaScript 中为 Object
实现 filter()
?
假设我有这个对象:
var foo = {
bar: "Yes"
};
并且我想编写一个适用于 Object
的 filter()
:
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>
ECMAScript 5 has the filter()
prototype for Array
types, but not Object
types, if I understand correctly.
How would I implement a filter()
for Object
s in JavaScript?
Let's say I have this object:
var foo = {
bar: "Yes"
};
And I want to write a filter()
that works on 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;
};
This works when I use it in the following demo, but when I add it to my site that uses jQuery 1.5 and jQuery UI 1.8.9, I get JavaScript errors in 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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
首先,扩展
对象被认为是不好的做法。原型
。相反,将您的功能作为独立功能提供,或者如果您确实想扩展全局功能,请将其作为Object
上的实用功能提供,就像已经有Object.keys
、Object.assign
、Object.is
、...等。我在这里提供了几种解决方案:
reduce
和Object.keys
Object.assign
map< /code> 和扩展语法而不是
reduce
Object.entries
和Object.fromEntries
1. 使用
reduce
和Object.keys
与
减少
和Object.keys
实现所需的过滤器(使用 ES6 箭头语法):请注意,在上面的代码中,谓词必须是包含条件(与OP使用的排除条件相反),以便它符合如何
Array.prototype.filter
有效。2. 如(1),结合
Object.assign
在上述解决方案中 逗号运算符用于
reduce
部分,以返回变异的res
对象。当然,这可以写成两个语句而不是一个表达式,但后者更简洁。要在不使用逗号运算符的情况下执行此操作,您可以使用Object.assign
,它确实返回变异对象:3. 使用
map
和 spread 语法代替reduce
这里我们将
Object.assign
调用移出循环,因此它只执行一次,并将各个键作为单独的参数传递给它(使用 扩展语法):4. 使用
Object.entries
和Object.fromEntries
由于该解决方案将对象转换为中间数组,然后将其转换回普通对象,因此使使用
Object.entries
< /a> (ES2017) 和相反的(即 从键/值对数组创建对象) href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries" rel="noreferrer">Object.fromEntries
(ES2019)。它导致了
Object
上的这个“one-liner”方法:此处谓词函数获取一个键/值对作为参数,这有点不同,但允许谓词函数的逻辑有更多可能性。
First of all, it's considered bad practice to extend
Object.prototype
. Instead, provide your feature as stand-alone function, or if you really want to extend a global, provide it as utility function onObject
, just like there already areObject.keys
,Object.assign
,Object.is
, ...etc.I provide here several solutions:
reduce
andObject.keys
Object.assign
map
and spread syntax instead ofreduce
Object.entries
andObject.fromEntries
1. Using
reduce
andObject.keys
With
reduce
andObject.keys
to implement the desired filter (using ES6 arrow syntax):Note that in the above code
predicate
must be an inclusion condition (contrary to the exclusion condition the OP used), so that it is in line with howArray.prototype.filter
works.2. As (1), in combination with
Object.assign
In the above solution the comma operator is used in the
reduce
part to return the mutatedres
object. This could of course be written as two statements instead of one expression, but the latter is more concise. To do it without the comma operator, you could useObject.assign
instead, which does return the mutated object:3. Using
map
and spread syntax instead ofreduce
Here we move the
Object.assign
call out of the loop, so it is only made once, and pass it the individual keys as separate arguments (using the spread syntax):4. Using
Object.entries
andObject.fromEntries
As the solution translates the object to an intermediate array and then converts that back to a plain object, it would be useful to make use of
Object.entries
(ES2017) and the opposite (i.e. create an object from an array of key/value pairs) withObject.fromEntries
(ES2019).It leads to this "one-liner" method on
Object
:The predicate function gets a key/value pair as argument here, which is a bit different, but allows for more possibilities in the predicate function's logic.
永远不要扩展
Object.prototype
。你的代码将会发生可怕的事情。事情会破裂。您正在扩展所有对象类型,包括对象文字。
这是您可以尝试的一个简单示例:
而是创建一个传递对象的函数。
Never ever extend
Object.prototype
.Horrible things will happen to your code. Things will break. You're extending all object types, including object literals.
Here's a quick example you can try:
Instead create a function that you pass the object.
从 2020 年开始,Vanilla JS 中的解决方案。
您可以按键过滤
romNumbers
对象:或按值过滤
romNumbers
对象:Solution in Vanilla JS from year 2020.
You can filter
romNumbers
object by key:Or filter
romNumbers
object by value:如果您愿意使用下划线或lodash,则可以使用
pick
(或其相反,省略
)。underscore 文档中的示例:
或者使用回调(对于 lodash,使用 pickBy) :
If you're willing to use underscore or lodash, you can use
pick
(or its opposite,omit
).Examples from underscore's docs:
Or with a callback (for lodash, use pickBy):
ES6 方法...
假设您有下面这个对象:
创建一个函数:
并调用它:
并且将返回:
ES6 approach...
Imagine you have this object below:
Create a function:
And call it:
and will return:
那么给出
:
Given
then :
正如帕特里克已经说过的那样,这是一个坏主意,因为它几乎肯定会破坏您可能希望使用的任何第三方代码。
如果您扩展
Object.prototype
,所有像jquery或prototype这样的库都会中断,原因是对象上的惰性迭代(没有hasOwnProperty
检查)将会中断,因为您添加的函数将中断成为迭代的一部分。As patrick already stated this is a bad idea, as it will almost certainly break any 3rd party code you could ever wish to use.
All libraries like jquery or prototype will break if you extend
Object.prototype
, the reason being that lazy iteration over objects (withouthasOwnProperty
checks) will break since the functions you add will be part of the iteration.普通 ES6:
Plain ES6:
怎么样:
或者...
How about:
Or...
我创建了一个
Object.filter()
,它不仅按函数进行过滤,而且还接受要包含的键数组。可选的第三个参数将允许您反转过滤器。给定:
数组:
函数:
代码
免责声明:为了简洁起见,我选择使用 Ext JS 核心。认为没有必要为对象类型编写类型检查器,因为它不是问题的一部分。
I have created an
Object.filter()
which does not only filter by a function, but also accepts an array of keys to include. The optional third parameter will allow you to invert the filter.Given:
Array:
Function:
Code
Disclaimer: I chose to use Ext JS core for brevity. Did not feel it was necessary to write type checkers for object types as it was not part of the question.
https://masteringjs.io/tutorials/fundamentals/filter-object
https://masteringjs.io/tutorials/fundamentals/filter-object
我固执己见的解决
方案:测试用例:
https://gist.github.com/bernardoadc/872d5a174108823159d845cc5baba337
My opinionated solution:
Test cases:
https://gist.github.com/bernardoadc/872d5a174108823159d845cc5baba337
如果您的文件中有
Symbol
属性对象,也应该被过滤,你可以不使用:Object.keys
Object.entries
Object.fromEntries
,...因为:您可以使用
Reflect.ownKeys< /code>
和
reduce
(打开 DevTools 进行日志输出 - 符号未记录在 Stackoverflow UI 上)
这等于
封装在
filter()
函数中,可以传递可选的target
对象If you have
Symbol
properties in your object, that should be filtered too, you can not use:Object.keys
Object.entries
Object.fromEntries
, ... because:You could use
Reflect.ownKeys
and filter keys inreduce
(Open DevTools for log output - Symbols are not logged on Stackoverflow UI)
This is equal to this
Wrapped in a
filter()
function, an optionaltarget
object could be passed您还可以执行类似的操作,在其中过滤条目以查找提供的键并返回值
You could also do something like this where you are filtering on the entries to find the key provided and return the value
如果您希望改变同一个对象而不是创建一个新对象。
以下示例将删除所有 0 或空值:
If you wish to mutate the same object rather than create a new one.
The following example will delete all 0 or empty values:
我只是想添加我的做法,因为它可以节省我创建额外的函数,我认为更干净,而且我没有看到这个答案:
最酷的是它也适用于对象数组:
I just wanted to add the way that I do it because it saves me creating extra functions, I think is cleaner and I didn't see this answer:
The cool thing is that also works on arrays of objects:
正如大家所说,不要搞砸原型。相反,只需编写一个函数即可执行此操作。这是我使用
lodash
的版本:Like everyone said, do not screw around with prototype. Instead, simply write a function to do so. Here is my version with
lodash
:如果你不需要原始对象,这是一个简单、非常无聊的答案,不会浪费内存:
如果你只过滤少量对象,并且你的对象没有很多键,你可能不会想要构造一个
Set
,在这种情况下使用array.includes
而不是set.has
。If you don't need the original object, this is a simple, very boring answer that doesn't waste memory:
If you're only filtering a small number of objects, and your objects don't have many keys, you might not want to bother with constructing a
Set
, in which case usearray.includes
instead ofset.has
.您可以使用 Lodash 的
_.omit()
函数来实现此目的,该函数会创建一个不包括指定键的新对象。具体操作方法如下:在此代码中,
_.omit(obj, 'bar')
创建一个新对象,从中排除键
对象,并且'bar'
objfilteredObj
将包含{ foo: 1 }
。You can achieve this using Lodash's
_.omit()
function, which creates a new object excluding the specified key. Here's how you can do it:In this code,
_.omit(obj, 'bar')
creates a new object excluding the key'bar'
from theobj
object, andfilteredObj
will contain{ foo: 1 }
.在这些情况下,我使用 jquery $.map,它可以处理对象。正如其他答案中提到的,更改本机原型并不是一个好的做法(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes)
下面是仅通过检查对象的某些属性进行过滤的示例。如果您的条件为真,它会返回自己的对象;如果条件不为真,它会返回
undefined
。undefined
属性将使该记录从您的对象列表中消失;In these cases I use the jquery $.map, which can handle objects. As mentioned on other answers, it's not a good practice to change native prototypes (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes)
Below is an example of filtering just by checking some property of your object. It returns the own object if your condition is true or returns
undefined
if not. Theundefined
property will make that record disappear from your object list;