在ES6中通过键过滤对象属性

假设我有一个对象:

{
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}

我想通过过滤上面的对象来创建另一个对象这样我就有了。

 {
item1: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}

我正在寻找一种干净的方法来实现这一点使用Es6,所以扩散操作符是可用的。

567358 次浏览

如果你有一个允许值的列表,你可以很容易地将它们保留在一个对象中:

const raw = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};


const allowed = ['item1', 'item3'];


const filtered = Object.keys(raw)
.filter(key => allowed.includes(key))
.reduce((obj, key) => {
obj[key] = raw[key];
return obj;
}, {});


console.log(filtered);

这个用途:

  1. Object.keys列出raw(原始数据)中的所有属性,然后
  2. Array.prototype.filter选择出现在允许列表中的键,使用
    1. Array.prototype.includes,以确保它们存在
    2. 李< / ol > < / >
    3. Array.prototype.reduce来构建一个只具有允许属性的新对象。

    这将使用允许的属性创建一个浅拷贝(但不会复制属性本身)。

    你也可以使用对象展开运算符创建一系列对象而不改变它们(感谢谢谢你提到这个):

    const raw = {
    item1: { key: 'sdfd', value:'sdfd' },
    item2: { key: 'sdfd', value:'sdfd' },
    item3: { key: 'sdfd', value:'sdfd' }
    };
    
    
    const allowed = ['item1', 'item3'];
    
    
    const filtered = Object.keys(raw)
    .filter(key => allowed.includes(key))
    .reduce((obj, key) => {
    return {
    ...obj,
    [key]: raw[key]
    };
    }, {});
    
    
    console.log(filtered);

    出于琐事的目的,如果你想从原始数据中删除不需要的字段(我不会建议这样做,因为它涉及一些丑陋的突变),你可以像这样反转includes检查:

    const raw = {
    item1: { key: 'sdfd', value:'sdfd' },
    item2: { key: 'sdfd', value:'sdfd' },
    item3: { key: 'sdfd', value:'sdfd' }
    };
    
    
    const allowed = ['item1', 'item3'];
    
    
    Object.keys(raw)
    .filter(key => !allowed.includes(key))
    .forEach(key => delete raw[key]);
    
    
    console.log(raw);

    我使用这个示例是为了展示一个基于突变的解决方案,但我不建议使用它。

你可以添加一个泛型ofilter(用泛型oreduce实现),这样你就可以像过滤数组一样轻松地过滤对象

const oreduce = (f, acc, o) =>
Object
.entries (o)
.reduce
( (acc, [ k, v ]) => f (acc, v, k, o)
, acc
)


const ofilter = (f, o) =>
oreduce
( (acc, v, k, o)=>
f (v, k, o)
? Object.assign (acc, {[k]: v})
: acc
, {}
, o
)

我们可以看到它在这里工作

const data =
{ item1: { key: 'a', value: 1 }
, item2: { key: 'b', value: 2 }
, item3: { key: 'c', value: 3 }
}


console.log
( ofilter
( (v, k) => k !== 'item2'
, data
)
// [ { item1: { key: 'a', value: 1 } }
// , { item3: { key: 'c', value: 3 } }
// ]


, ofilter
( x => x.value === 3
, data
)
// [ { item3: { key: 'c', value: 3 } } ]
)

在您自己的浏览器中验证下面的结果

const oreduce = (f, acc, o) =>
Object
.entries (o)
.reduce
( (acc, [ k, v ]) => f (acc, v, k, o)
, acc
)


const ofilter = (f, o) =>
oreduce
( (acc, v, k, o)=>
f (v, k, o)
? Object.assign (acc, { [k]: v })
: acc
, {}
, o
)


const data =
{ item1: { key: 'a', value: 1 }
, item2: { key: 'b', value: 2 }
, item3: { key: 'c', value: 3 }
}


console.log
( ofilter
( (v, k) => k !== 'item2'
, data
)
// [ { item1: { key: 'a', value: 1 } }
// , { item3: { key: 'c', value: 3 } }
// ]


, ofilter
( x => x.value === 3
, data
)
// [ { item3: { key: 'c', value: 3 } } ]
)

这两个功能可以通过多种方式实现。我选择在oreduce中附加到Array.prototype.reduce,但你也可以很容易地从头开始编写它

如果你可以使用ES6语法,我发现最干净的方法是这样做,如前所述在这里在这里是:

const data = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};


const { item2, ...newData } = data;

现在,newData包含:

{
item1: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};

或者,如果你将键存储为字符串:

const key = 'item2';
const { [key]: _, ...newData } = data;

在后一种情况下,[key]被转换为item2,但由于你使用的是const赋值,你需要为赋值指定一个名称。_表示丢弃值。

更普遍的:

const { item2, ...newData } = data; // Assign item2 to item2
const { item2: someVarName, ...newData } = data; // Assign item2 to someVarName
const { item2: _, ...newData } = data; // Assign item2 to _
const { ['item2']: _, ...newData } = data; // Convert string to key first, ...

这不仅将您的操作减少到一行程序,而且还不需要知道其他键是什么(那些您想要保留的键)。

一个简单的效用函数是这样的:

function removePropFromObject(obj, prop) {
const { [prop]: _, ...rest } = obj
return { ...rest }
}

我最近是这样做的:

const dummyObj = Object.assign({}, obj);
delete dummyObj[key];
const target = Object.assign({}, {...dummyObj});

你能找到的最干净的方法是Lodash #选择

const _ = require('lodash');


const allowed = ['item1', 'item3'];


const obj = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}


const filteredObj = _.pick(obj, allowed)

没有什么之前没有说过的,但是把一些答案结合到ES6的一般答案中:

const raw = {
item1: { key: 'sdfd', value: 'sdfd' },
item2: { key: 'sdfd', value: 'sdfd' },
item3: { key: 'sdfd', value: 'sdfd' }
};


const filteredKeys = ['item1', 'item3'];


const filtered = filteredKeys
.reduce((obj, key) => ({ ...obj, [key]: raw[key] }), {});


console.log(filtered);

利用ssube的回答

这是一个可重用的版本。

Object.filterByKey = function (obj, predicate) {
return Object.keys(obj)
.filter(key => predicate(key))
.reduce((out, key) => {
out[key] = obj[key];
return out;
}, {});
}

叫它use

const raw = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};


const allowed = ['item1', 'item3'];


var filtered = Object.filterByKey(raw, key =>
return allowed.includes(key));
});


console.log(filtered);

ES6箭头函数的美妙之处在于,你不必将allowed作为参数传递。

好吧,这样怎么样:

const myData = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};


function filteredObject(obj, filter) {
if(!Array.isArray(filter)) {
filter = [filter.toString()];
}
const newObj = {};
for(i in obj) {
if(!filter.includes(i)) {
newObj[i] = obj[i];
}
}
return newObj;
}

像这样叫它:

filteredObject(myData, ['item2']); //{item1: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' }}

只是一行没有外部库的现代JS中的另一个解决方案。

我正在玩“解构”功能:

const raw = {
item1: { key: 'sdfd', value: 'sdfd' },
item2: { key: 'sdfd', value: 'sdfd' },
item3: { key: 'sdfd', value: 'sdfd' }
};
var myNewRaw = (({ item1, item3}) => ({ item1, item3 }))(raw);
console.log(myNewRaw);

有很多方法可以做到这一点。接受的答案使用键-过滤-还原方法,这不是性能最好的方法。

相反,使用for...in循环遍历对象的键,或遍历允许的键,并且然后组合一个新对象的性能比__abc2高出50%。

const obj = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};


const keys = ['item1', 'item3'];


function keysReduce (obj, keys) {
return keys.reduce((acc, key) => {
if(obj[key] !== undefined) {
acc[key] = obj[key];
}
return acc;
}, {});
};


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


keysReduce(obj, keys);   // Faster if the list of allowed keys are short
forInCompose(obj, keys); // Faster if the number of object properties are low

a.参见jsPerf查看简单用例的基准测试。不同浏览器的结果会有所不同。

这里的答案肯定是合适的,但它们有点慢,因为它们需要对对象中的每个属性遍历白名单。下面的解决方案对于大型数据集来说要快得多,因为它只在白名单中循环一次:

const data = {
allowed1: 'blah',
allowed2: 'blah blah',
notAllowed: 'woah',
superSensitiveInfo: 'whooooah',
allowed3: 'bleh'
};


const whitelist = ['allowed1', 'allowed2', 'allowed3'];


function sanitize(data, whitelist) {
return whitelist.reduce(
(result, key) =>
data[key] !== undefined
? Object.assign(result, { [key]: data[key] })
: result,
{}
);
}


const result = sanitize(data, whitelist);


console.log(result);

你可以这样做:

const base = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};


const filtered = (
source => {
with(source){
return {item1, item3}
}
}
)(base);


// one line
const filtered = (source => { with(source){ return {item1, item3} } })(base);

这是可行的,但不是很清楚,加上with语句不推荐(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with)。

一个不使用filter的更简单的解决方案可以用Object.entries()代替Object.keys()

const raw = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};


const allowed = ['item1', 'item3'];


const filtered = Object.entries(raw).reduce((acc,elm)=>{
const [k,v] = elm
if (allowed.includes(k)) {
acc[k] = v
}
return acc
},{})

这个函数将根据键列表筛选对象,它比前面的答案更有效,因为它不需要使用Array。在调用reduce之前进行筛选。所以它是O(n)而不是O(n +过滤)

function filterObjectByKeys (object, keys) {
return Object.keys(object).reduce((accum, key) => {
if (keys.includes(key)) {
return { ...accum, [key]: object[key] }
} else {
return accum
}
}, {})
}

好吧,这一行怎么样

    const raw = {
item1: { key: 'sdfd', value: 'sdfd' },
item2: { key: 'sdfd', value: 'sdfd' },
item3: { key: 'sdfd', value: 'sdfd' }
};


const filteredKeys = ['item1', 'item3'];


const filtered = Object.assign({}, ...filteredKeys.map(key=> ({[key]:raw[key]})));

在循环过程中,当遇到某些属性/键时,不返回任何内容,并继续执行其余的:

const loop = product =>
Object.keys(product).map(key => {
if (key === "_id" || key === "__v") {
return;
}
return (
<ul className="list-group">
<li>
{product[key]}
<span>
{key}
</span>
</li>
</ul>
);
});

你现在可以通过使用< em > Object.fromEntries < / em >方法(检查浏览器支持)使它更短更简单:

const raw = { item1: { prop:'1' }, item2: { prop:'2' }, item3: { prop:'3' } };


const allowed = ['item1', 'item3'];


const filtered = Object.fromEntries(
Object.entries(raw).filter(
([key, val])=>allowed.includes(key)
)
);

阅读更多关于:Object.fromEntries

您可以删除对象上的特定属性

items={
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}


// Example 1
var key = "item2";
delete items[key];


// Example 2
delete items["item2"];


// Example 3
delete items.item2;
const filteredObject = Object.fromEntries(Object.entries(originalObject).filter(([key, value]) => key !== uuid))

简单的方法!这样做。

const myData = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};
const{item1,item3}=myData
const result =({item1,item3})

另一种方法是使用Array.prototype.forEach() as

const raw = {
item1: {
key: 'sdfd',
value: 'sdfd'
},
item2: {
key: 'sdfd',
value: 'sdfd'
},
item3: {
key: 'sdfd',
value: 'sdfd'
}
};


const allowed = ['item1', 'item3', 'lll'];


var finalObj = {};
allowed.forEach(allowedVal => {
if (raw[allowedVal])
finalObj[allowedVal] = raw[allowedVal]
})
console.log(finalObj)

它只包含原始数据中可用的那些键的值,从而防止添加任何垃圾数据。

我很惊讶居然没有人提出这个建议。它非常干净,非常明确地告诉你想要保留哪些键。

const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}


const filterObject = ({a,b,c}) => ({a,b,c})
const filteredObject = filterObject(unfilteredObject)

或者如果你想要一个脏的眼线笔:

const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}


const filteredObject = (({a,b,c})=>({a,b,c}))(unfilteredObject);

在允许的键上使用Array.reduce方法的另一个解决方案:

const raw = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};


const allowed = ['item1', 'item3'];


const filtered = allowed.reduce((obj, key) => {
obj[key] = raw[key];
return obj
}, {})


console.log(filtered);

特别是对于较大的源对象(在本例中为原始对象),这是有意义的。迭代不会使用源的所有条目执行,而只使用您想要过滤的键,因此更短/更快…

演示this Fiddle


但我不得不说,我也喜欢这个答案中的解决方案,它使用Object.fromEntries Array.filterArray.includes:

const object = Object.fromEntries(
Object.entries(raw).filter(([key, value]) => allowed.includes(key))
);

演示this Fiddle

这就是我的解决方案:

const filterObject = (obj, condition) => {
const filteredObj = {};
Object.keys(obj).map(key => {
if (condition(key)) {
dataFiltered[key] = obj[key];
}
});
return filteredObj;
}

另一条捷径

function filterByKey(v,keys){
const newObj ={};
keys.forEach(key=>{v[key]?newObj[key]=v[key]:''});
return newObj;
}


//given
let obj ={ foo: "bar", baz: 42,baz2:"blabla" , "spider":"man", monkey:true};


//when
let outObj =filterByKey(obj,["bar","baz2","monkey"]);


//then
console.log(outObj);
//{
//  "baz2": "blabla",
//  "monkey": true
//}

我知道这个问题已经有很多答案了,abc0是一个相当老的问题。但我刚想出了一句简洁的俏皮话

JSON.parse(JSON.stringify(raw, ['key', 'value', 'item1', 'item3']))

这将返回另一个具有白名单属性的对象。注意,keyvalue包含在列表中。

下面的方法获取要过滤的对象和任何属性。

function removeObjectKeys(obj, ...keysToRemove) {
let mObject = { ...obj }
for (let key of keysToRemove) {
const { [String(key)]: _, ...rest } = mObject
mObject = { ...rest }
}
return mObject
}
    

const obj = { 123: "hello", 345: "world", 567: "and kitty" };
const filtered = removeObjectKeys(obj, 123);
console.log(filtered);


const twoFiltered = removeObjectKeys(obj, 345, 567);
console.log(twoFiltered);

使用PropPick


pick('item1 item3', obj);
// {
//   item1: { key: 'sdfd', value:'sdfd' },
//   item3: { key: 'sdfd', value:'sdfd' }
// }


上面的许多解决方案对于raw中的每个键都重复调用Array.prototype.includes,这将使解决方案O(n·m)(其中n是对象中的键数,m是允许列表的长度)。

这可以通过使用一个允许的Set来避免,但是遍历allowed键并将它们复制到一个初始为空的对象,会得到非常简单、可读的代码,即O(m):

const raw = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};


const allowed = ['item1', 'item3'];


const filtered = {};
for (const key of allowed) {
if (key in raw) filtered[key] = raw[key];
}


console.log(filtered);

如果你想避免复制继承的属性,你也可以使用raw.hasOwnProperty(key)代替key in raw

基于以下两个答案:

< p > https://stackoverflow.com/a/56081419/13819049
https://stackoverflow.com/a/54976713/13819049 < / p >

我们可以:

const original = { a: 1, b: 2, c: 3 };
const allowed = ['a', 'b'];


const filtered = Object.fromEntries(allowed.map(k => [k, original[k]]));

哪个更干净更快:

< a href = " https://jsbench。我/ swkv2cbgkd / 1”rel = " https://jsbench.me/swkv2cbgkd/1 noreferrer " > < / >