JavaScript: filter()用于对象

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

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

假设我有这个对象:

var foo = {
bar: "Yes"
};

我想写一个适用于__abc1的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>

694218 次浏览

永远不要扩展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已经说过的,这是一个坏主意,因为它几乎肯定会破坏任何第三方代码,你可能希望使用。

如果你扩展Object.prototype,所有像jquery或prototype这样的库都会中断,原因是对象上的惰性迭代(没有hasOwnProperty检查)会中断,因为你添加的函数将是迭代的一部分。

我已经创建了一个Object.filter(),它不仅通过函数进行筛选,而且还接受一个包含键的数组。可选的第三个参数将允许反转筛选器。

考虑到:

var foo = {
x: 1,
y: 0,
z: -1,
a: 'Hello',
b: 'World'
}

数组:

Object.filter(foo, ['z', 'a', 'b'], true);

功能:

Object.filter(foo, function (key, value) {
return Ext.isString(value);
});

代码

< em >免责声明:为了简洁起见,我选择使用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>

首先,扩展Object.prototype被认为是不好的做法。相反,将你的特性作为独立函数提供,或者如果你真的想扩展一个全局函数,就在Object上提供它作为实用函数,就像已经有Object.keysObject.assignObject.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)和相反的从键/值对数组创建对象 (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);

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

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

来自underscore文档的例子:

_.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}

鉴于

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


keys = ['firstname', 'age'];

然后:

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

如何:

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

还是……

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

在这些情况下,我使用jquery $。映射,它可以处理对象。正如在其他回答中提到的,更改本机原型不是一个好的实践(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes)

下面是一个仅通过检查对象的某些属性进行过滤的示例。如果你的条件为真,它返回自己的对象,否则返回undefinedundefined属性将使该记录从对象列表中消失;

$.map(yourObject, (el, index)=>{
return el.yourProperty ? el : undefined;
});

我的观点是:

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
}

测试用例:

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

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"
}
}

就像所有人说的,不要在原型上做文章。相反,只需编写一个函数来执行此操作。下面是我使用lodash的版本:

import each from 'lodash/each';
import get from 'lodash/get';


const myFilteredResults = results => {
const filteredResults = [];


each(results, obj => {
// filter by whatever logic you want.


// sample example
const someBoolean = get(obj, 'some_boolean', '');


if (someBoolean) {
filteredResults.push(obj);
}
});


return filteredResults;
};

如果您希望改变相同的对象,而不是创建一个新的对象。

下面的例子将删除所有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);

从2020年开始,香草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}

平原ES6:

var foo = {
bar: "Yes"
};


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


console.log(res)
// ["bar"]

如果你的对象中有Symbol属性,它也应该被过滤,你可以使用: Object.keys Object.entries Object.fromEntries,…因为:

Symbol键不是可列举的 !

你可以在reduce中使用Reflect.ownKeys和过滤键

Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && {...a, [k]: o[k]} || a, {});

(打开DevTools输出日志- Stackoverflow界面上不记录符号)

const bKey = Symbol('b_k');
const o = {
a:                 1,
[bKey]:            'b',
c:                 [1, 3],
[Symbol.for('d')]: 'd'
};


const allow = ['a', bKey, Symbol.for('d')];


const z1 = Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && {...a, [k]: o[k]} || a, {});


console.log(z1);                   // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
console.log(bKey in z1)            // true
console.log(Symbol.for('d') in z1) // true

这个等于这个

const z2 = Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && Object.assign(a, {[k]: o[k]}) || a, {});
const z3 = Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && Object.defineProperty(a, k, {value: o[k]}) || a, {});


console.log(z2); // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
console.log(z3); // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}

filter()函数中,可以传递一个可选的target对象

const filter = (o, allow, t = {}) => Reflect.ownKeys(o).reduce(
(a, k) => allow.includes(k) && {...a, [k]: o[k]} || a,
t
);


console.log(filter(o, allow));           // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
console.log(filter(o, allow, {e: 'e'})); // {a: 1, e: "e", Symbol(b_k): "b", Symbol(d): "d"}

如果你不需要原始对象,这是一个简单的,非常无聊的答案,不浪费内存:

const obj = {'a': 'want this', 'b': 'want this too', 'x': 'remove this'}
const keep = new Set(['a', 'b', 'c'])


function filterObject(obj, keep) {
Object.keys(obj).forEach(key => {
if (!keep.has(key)) {
delete obj[key]
}
})
}

如果你只过滤少量的对象,并且你的对象没有很多键,你可能不想构造Set,在这种情况下使用array.includes而不是set.has

您还可以执行类似的操作,对条目进行筛选,以找到提供的键并返回值

   let func = function(items){
let val
Object.entries(this.items).map(k => {
if(k[0]===kind){
val = k[1]
}
})
return val
}
    var foo = {
bar: "Yes",
pipe: "No"
};


const ret =  Object.entries(foo).filter(([key, value])=> value === 'Yes');

< a href = " https://masteringjs。Io /tutorials/fundamentals/filter-object" rel="nofollow noreferrer">https://masteringjs.io/tutorials/fundamentals/filter-object .