简体   繁体   中英

JavaScript Filter array of objects based on property of another array of objects

I have 2 array of objects

Tasks
[{
 name: "test",
 type: "one"
},
{
 name: "test2",
 type: 'two'
}]

activities

[{
 name: "test",
 type: "one"
},
{
 name: "test2",
 type: 'two'
},
{
 name: "test3",
 type: "three"
 }]

How can I filter the activities array by properties of the tasks array? I would like this outcome.

activities

[{
 name: "test",
 type: "one"
},
{
 name: "test2",
 type: 'two'
}]

I have tried using .filter(), but that returns an empty collection.

let tests = activities.filter(x => tasks.includes(x.type));

How can I achieve that in Javascript?

filter is the right tool, but includes is not. The entries in tasks are objects, not strings; none of them will match any of the types in tests .

Instead, the simple version uses some to see if any entries in tasks match the type of the activity being tested:

let tests = activities.filter(a => tasks.some(t => t.type == a.type));

Live Example:

 const tasks = [ { name: "test", type: "one" }, { name: "test2", type: 'two' } ]; const activities = [ { name: "test", type: "one" }, { name: "test2", type: 'two' }, { name: "test3", type: "three" } ]; let tests = activities.filter(a => tasks.some(t => t.type == a.type)); console.log(tests); 
 .as-console-wrapper { max-height: 100% !important; } 

Naturally, that means at least partially re-traversing tasks for each entry in activities . For the examples you've shown, that's absolutely fine; if tasks is hundreds of thousands of entries long or this is done in a tight loop, not so much. :-)

In that situation, you could give yourself a set of known types from tasks in advance, then just test against the set:

const knownTypes = new Set();
for (const task of tasks) {
    knownTypes.add(task.type);
}
let tests = activities.filter(a => knownTypes.has(a.type));

Live Example:

 const tasks = [ { name: "test", type: "one" }, { name: "test2", type: 'two' } ]; const activities = [ { name: "test", type: "one" }, { name: "test2", type: 'two' }, { name: "test3", type: "three" } ]; const knownTypes = new Set(); for (const task of tasks) { knownTypes.add(task.type); } let tests = activities.filter(a => knownTypes.has(a.type)); console.log(tests); 
 .as-console-wrapper { max-height: 100% !important; } 

Alternately, instead of Set you could also use an object for knownTypes , creating it via Object.create(null) on the off-chance any of your types happens to be something with the same name as a property on Object.prototype :

const knownTypes = Object.create(null);
for (const task of tasks) {
    knownTypes[task.type] = true;
}
let tests = activities.filter(a => knownTypes[a.type]);

Live Example:

 const tasks = [ { name: "test", type: "one" }, { name: "test2", type: 'two' } ]; const activities = [ { name: "test", type: "one" }, { name: "test2", type: 'two' }, { name: "test3", type: "three" } ]; const knownTypes = Object.create(null); for (const task of tasks) { knownTypes[task.type] = true; } let tests = activities.filter(a => knownTypes[a.type]); console.log(tests); 
 .as-console-wrapper { max-height: 100% !important; } 

Almost there. Try using the .some array method to check if the item is present, since includes won't work for a nested property.

 const tasks = [{ name: "test", type: "one" }, { name: "test2", type: 'two' }] const activities = [{ name: "test", type: "one" }, { name: "test2", type: 'two' }, { name: "test3", type: "three" }] let tests = activities.filter(x => tasks.some(t => t.type === x.type)); console.log(tests) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM