JavaScript:用于对象的 filter()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5072136/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaScript: filter() for Objects
提问by AgileMeansDoAsLittleAsPossible
ECMAScript 5 has the filter()
prototype for Array
types, but not Object
types, if I understand correctly.
如果我理解正确的话,ECMAScript 5 有类型的filter()
原型Array
,但没有Object
类型。
How would I implement a filter()
for Object
s in JavaScript?
我将如何在 JavaScript 中实现filter()
for Object
s?
Let's say I have this object:
假设我有这个对象:
var foo = {
bar: "Yes"
};
And I want to write a filter()
that works on Object
s:
我想写一个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;
};
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.
当我在下面的演示中使用它时,这会起作用,但是当我将它添加到我使用 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>
回答by trincot
First of all, it's considered bad practice to extend Object.prototype
. Instead, provide your feature as utility function on Object
, just like there already are Object.keys
, Object.assign
, Object.is
, ...etc.
首先,扩展Object.prototype
. 取而代之的是,将您的功能作为 上的实用程序功能提供Object
,就像已经有Object.keys
, Object.assign
, Object.is
, ... 等一样。
I provide here several solutions:
我在这里提供了几种解决方案:
- Using
reduce
andObject.keys
- As (1), in combination with
Object.assign
- Using
map
and spread syntax instead ofreduce
- Using
Object.entries
andObject.fromEntries
- 使用
reduce
和Object.keys
- 如(1),结合
Object.assign
- 使用
map
和传播语法而不是reduce
- 使用
Object.entries
和Object.fromEntries
1. Using reduce
and Object.keys
1. 使用reduce
和Object.keys
With reduce
and Object.keys
to implement the desired filter (using ES6 arrow syntax):
使用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);
Note that in the above code predicate
must be an inclusioncondition (contrary to the exclusioncondition the OP used), so that it is in line with how Array.prototype.filter
works.
请注意,在上面的代码中predicate
必须是包含条件(与OP 使用的排除条件相反),以便它符合Array.prototype.filter
工作原理。
2. As (1), in combination with Object.assign
2.如(1),结合 Object.assign
In the above solution the comma operatoris used in the reduce
part to return the mutated res
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 use Object.assign
instead, which doesreturn the mutated object:
在上述解决方案中,逗号运算符用于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. Using map
and spread syntax instead of reduce
3.使用map
和传播语法代替reduce
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):
在这里,我们将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. Using Object.entries
and Object.fromEntries
4. 使用Object.entries
和Object.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) with Object.fromEntries
(ES2019).
由于解决方案将对象转换为中间数组,然后将其转换回普通对象,因此使用Object.entries
(ES2017) 和相反的(即从键/值对数组创建对象)和Object.fromEntries
( ES2019)。
It leads to this "one-liner" method on Object
:
它导致了这种“单行”方法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);
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.
谓词函数在这里获取一个键/值对作为参数,这有点不同,但允许谓词函数的逻辑中有更多的可能性。
回答by user113716
Never ever extend Object.prototype
.
永远不要扩展Object.prototype
。
Horrible things will happen to your code. Things will break. You're extending allobject types, including object literals.
你的代码会发生可怕的事情。事情会破裂。您正在扩展所有对象类型,包括对象文字。
Here's a quick example you can try:
这是您可以尝试的快速示例:
// 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!"
Instead create a function that you pass the object.
而是创建一个传递对象的函数。
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;
};
回答by Bogdan D
If you're willing to use underscoreor lodash, you can use pick
(or its opposite, omit
).
如果您愿意使用underscore或lodash,则可以使用pick
(或其相反的omit
)。
Examples from underscore's docs:
下划线文档中的示例:
_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
// {name: 'moe', age: 50}
Or with a callback (for lodash, use pickBy):
或者使用回调(对于lodash,使用pickBy):
_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
return _.isNumber(value);
});
// {age: 50}
回答by Martin Jespersen
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.
正如帕特里克已经说过的那样,这是一个坏主意,因为它几乎肯定会破坏您可能希望使用的任何 3rd 方代码。
All libraries like jquery or prototype will break if you extend Object.prototype
, the reason being that lazy iteration over objects (without hasOwnProperty
checks) will break since the functions you add will be part of the iteration.
如果您扩展Object.prototype
,所有像 jquery 或原型这样的库都会中断,原因是对象上的延迟迭代(没有hasOwnProperty
检查)会中断,因为您添加的函数将成为迭代的一部分。
回答by Alireza
ES6approach...
ES6方法...
Imagine you have this object below:
想象一下你有下面这个对象:
const developers = {
1: {
id: 1,
name: "Brendan",
family: "Eich"
},
2: {
id: 2,
name: "John",
family: "Resig"
},
3: {
id: 3,
name: "Alireza",
family: "Dezfoolian"
}
};
Create a function:
创建一个函数:
const filterObject = (obj, filter, filterValue) =>
Object.keys(obj).reduce((acc, val) =>
(obj[val][filter] === filterValue ? acc : {
...acc,
[val]: obj[val]
}
), {});
And call it:
并称之为:
filterObject(developers, "name", "Alireza");
and will return:
并将返回:
{
1: {
id: 1,
name: "Brendan",
family: "Eich"
},
2: {
id: 2,
name: "John",
family: "Resig"
}
}
回答by shaunw
How about:
怎么样:
function filterObj(keys, obj) {
const newObj = {};
for (let key in obj) {
if (keys.includes(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
Or...
或者...
function filterObj(keys, obj) {
const newObj = {};
Object.keys(obj).forEach(key => {
if (keys.includes(key)) {
newObj[key] = obj[key];
}
});
return newObj;
}
回答by Mr. Polywhirl
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.
我创建了一个Object.filter()
不仅按函数过滤,而且还接受要包含的键数组。可选的第三个参数将允许您反转过滤器。
Given:
鉴于:
var foo = {
x: 1,
y: 0,
z: -1,
a: 'Hello',
b: 'World'
}
Array:
大批:
Object.filter(foo, ['z', 'a', 'b'], true);
Function:
功能:
Object.filter(foo, function (key, value) {
return Ext.isString(value);
});
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.
免责声明:为简洁起见,我选择使用 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>
回答by Abdennour TOUMI
Given
给定的
object = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};
keys = ['firstname', 'age'];
then :
然后 :
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')
)
回答by Z. Khullah
My opinionated solution:
我自以为是的解决方案:
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
}
Test cases:
测试用例:
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/bernardoadc/872d5a174108823159d845cc5baba337
https://gist.github.com/bernardoadc/872d5a174108823159d845cc5baba337
回答by yairniz
If you wish to mutate the same object rather than create a new one.
如果您希望改变同一个对象而不是创建一个新对象。
The following example will delete all 0 or empty values:
以下示例将删除所有 0 或空值:
const sev = { a: 1, b: 0, c: 3 };
const deleteKeysBy = (obj, predicate) =>
Object.keys(obj)
.forEach( (key) => {
if (predicate(obj[key])) {
delete(obj[key]);
}
});
deleteKeysBy(sev, val => !val);