admin管理员组文章数量:1125717
Let's say I have an object:
{
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}
I want to create another object by filtering the object above so I have something like.
{
item1: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}
I am looking for a clean way to accomplish this using Es6, so spread operators are available to me.
Let's say I have an object:
{
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}
I want to create another object by filtering the object above so I have something like.
{
item1: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}
I am looking for a clean way to accomplish this using Es6, so spread operators are available to me.
Share Improve this question edited Apr 19, 2021 at 6:20 Henke - Нава́льный П с м 5,7376 gold badges40 silver badges51 bronze badges asked Aug 3, 2016 at 18:02 29er29er 8,98512 gold badges49 silver badges65 bronze badges 4- ES6 has no object spread operators, and you don't need them here anyway – Bergi Commented Aug 3, 2016 at 18:32
- 2 Possible duplicate of JavaScript: filter() for Objects – Jonathan H Commented Mar 5, 2017 at 9:45
- @DanDascalescu But this answer gives an ES6 way of accomplishing what the OP asks, doesn't it? – Jonathan H Commented May 12, 2017 at 11:27
- 2 What if I wanted to filter by a key/value? – jmchauv Commented Apr 3, 2019 at 2:15
31 Answers
Reset to default 1 2 Next 933 +150If you have a list of allowed values, you can easily retain them in an object using:
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);
This uses:
Object.keys
to list all properties inraw
(the original data), thenArray.prototype.filter
to select keys that are present in the allowed list, usingArray.prototype.includes
to make sure they are present
Array.prototype.reduce
to build a new object with only the allowed properties.
This will make a shallow copy with the allowed properties (but won't copy the properties themselves).
You can also use the object spread operator to create a series of objects without mutating them (thanks to rjerue for mentioning this):
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);
For purposes of trivia, if you wanted to remove the unwanted fields from the original data (which I would not recommend doing, since it involves some ugly mutations), you could invert the includes
check like so:
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);
I'm including this example to show a mutation-based solution, but I don't suggest using it.
If you are OK with using ES6 syntax, I find that the cleanest way to do this, as noted here and here is:
const data = {
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};
const { item2, ...newData } = data;
Now, newData
contains:
{
item1: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
};
Or, if you have the key stored as a string:
const key = 'item2';
const { [key]: _, ...newData } = data;
In the latter case, [key]
is converted to item2
but since you are using a const
assignment, you need to specify a name for the assignment. _
represents a throw away value.
More generally:
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, ...
Not only does this reduce your operation to a one-liner but it also doesn't require you to know what the other keys are (those that you want to preserve).
A simple utility function would look like this:
function removePropFromObject(obj, prop) {
const { [prop]: _, ...rest } = obj
return { ...rest }
}
You can now make it shorter and simpler by using the Object.fromEntries method (check browser support):
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)
)
);
read more about: Object.fromEntries
Nothing that hasn't been said before, but to combine some answers to a general ES6 answer:
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);
The cleanest way you can find is with Lodash#pick
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)
Just another solution in one line of Modern JS with no external libraries.
I was playing with "Destructuring" feature :
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);
Building on these two answers:
https://stackoverflow.com/a/56081419/13819049
https://stackoverflow.com/a/54976713/13819049
We can do:
const original = { a: 1, b: 2, c: 3 };
const allowed = ['a', 'b'];
const filtered = Object.fromEntries(allowed.map(k => [k, original[k]]));
Which is cleaner and faster:
https://jsbench.me/swkv2cbgkd/1
ok, how about this one-liner
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]})));
Another solution using the Array.reduce
method on the allowed keys:
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);
Especially in case of larger source objects (raw in this example) it would make sense. The iteration will not be executed using all the entries of the source, but only using the keys you want to filter, thus shorter/faster...
Demonstration in this Fiddle...
But I do have to say I also like the solution in this answer here which is using Object.fromEntries
Array.filter
and Array.includes
:
const object = Object.fromEntries( Object.entries(raw).filter(([key, value]) => allowed.includes(key)) );
Demonstration in this Fiddle...
You can add a generic ofilter
(implemented with generic oreduce
) so you can easily filter objects the same way you can arrays –
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
)
We can see it working here -
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 } } ]
)
Verify the results in your own browser below –
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 } } ]
)
These two functions could be implemented in many ways. I chose to attach to Array.prototype.reduce
inside oreduce
but you could just as easily write it all from scratch
The answers here are definitely suitable but they are a bit slow because they require looping through the whitelist for every property in the object. The solution below is much quicker for large datasets because it only loops through the whitelist once:
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);
This is how I did it, recently:
const dummyObj = Object.assign({}, obj);
delete dummyObj[key];
const target = Object.assign({}, {...dummyObj});
A simpler solution without using filter
can be achieved with Object.entries()
instead of 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
},{})
Piggybacking on ssube's answer.
Here's a reusable version.
Object.filterByKey = function (obj, predicate) {
return Object.keys(obj)
.filter(key => predicate(key))
.reduce((out, key) => {
out[key] = obj[key];
return out;
}, {});
}
To call it 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);
The beautiful thing about ES6 arrow functions is that you don't have to pass in allowed
as a parameter.
Simple Way! To do this.
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})
You can do something like this:
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);
This works but is not very clear, plus the with
statement is not recommended (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with).
I know that this already has plenty of answers and is a rather old question. But I just came up with this neat one-liner:
JSON.parse(JSON.stringify(raw, ['key', 'value', 'item1', 'item3']))
That returns another object with just the whitelisted attributes. Note that the key
and value
is included in the list.
const filteredObject = Object.fromEntries(Object.entries(originalObject).filter(([key, value]) => key !== uuid))
There are many ways to accomplish this. The accepted answer uses a Keys-Filter-Reduce approach, which is not the most performant.
Instead, using a for...in
loop to loop through keys of an object, or looping through the allowed keys, and then composing a new object is ~50% more performanta.
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. See jsPerf for the benchmarks of a simple use case. Results will differ based on browsers.
Another short way
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
//}
You can remove a spesific property on your object
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;
The following method takes the object and any properties to filter out.
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);
Many of the above solutions repeatedly call Array.prototype.includes
for each key in raw
, which will make the solution O(n·m) (where n is the number of keys in the object an m is the length of the allowed list).
This could be avoided by using an allowed Set, but iterating over the allowed
keys and copying them to an initially-empty object gives very simple, readable code that is 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);
You could also use raw.hasOwnProperty(key)
instead of key in raw
if you wanted to avoid copying inherited properties.
OK, how about this:
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;
}
and call it like this:
filteredObject(myData, ['item2']); //{item1: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' }}
This function will filter an object based on a list of keys, its more efficient than the previous answer as it doesn't have to use Array.filter before calling reduce. so its O(n) as opposed to O(n + filtered)
function filterObjectByKeys (object, keys) {
return Object.keys(object).reduce((accum, key) => {
if (keys.includes(key)) {
return { ...accum, [key]: object[key] }
} else {
return accum
}
}, {})
}
I'm surprised how nobody has suggested this yet. It's super clean and very explicit about which keys you want to keep.
const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}
const filterObject = ({a,b,c}) => ({a,b,c})
const filteredObject = filterObject(unfilteredObject)
Or if you want a dirty one liner:
const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}
const filteredObject = (({a,b,c})=>({a,b,c}))(unfilteredObject);
During loop, return nothing when certain properties/keys are encountered and continue with the rest:
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>
);
});
Another approach would be to use 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)
It includes values of only those keys which are available in the raw data and thus preventing adding any junk data.
That would be my solution:
const filterObject = (obj, condition) => {
const filteredObj = {};
Object.keys(obj).map(key => {
if (condition(key)) {
dataFiltered[key] = obj[key];
}
});
return filteredObj;
}
use PropPick package
pick('item1 item3', obj);
// {
// item1: { key: 'sdfd', value:'sdfd' },
// item3: { key: 'sdfd', value:'sdfd' }
// }
本文标签: javascriptFilter object properties by key in ES6Stack Overflow
版权声明:本文标题:javascript - Filter object properties by key in ES6 - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736673278a1947038.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论