Problem:
I want to filter one array of objects using another array of objects, like in the following code snippet:
const entries = [
{
id: 1,
name: "Entry 1",
topics: [
{
id: 101,
name: "topic 1.1",
},
{
id: 102,
name: "topic 1.2",
},
{
id: 103,
name: "topic 1.3",
},
],
},
{
id: 2,
name: "Entry 2",
topics: [
{
id: 201,
name: "topic 2.1",
},
{
id: 202,
name: "topic 2.2",
},
{
id: 203,
name: "topic 2.3",
},
],
},
{
id: 3,
name: "Entry 3",
topics: [
{
id: 25,
name: "topic 3.1",
},
{
id: 26,
name: "topic 3.2",
},
],
},
];
const filters = [
{
id: 1,
topics: [
{
id: 101,
},
{
id: 102,
},
],
},
{
id: 2,
topics: [],
},
];
const result = entries
.filter(({ id, topics }) =>
filters.some(
({ id: idFromFilteres, topics: topicsFromFilters }) =>
idFromFilteres === id && topicsFromFilters.length > 0,
),
)
.map(({ id, topics, ...rest }) => ({
id,
topics: topics?.filter(({ id: topicId }) =>
filters.some(({ topics: topicsFromFilter }) =>
topicsFromFilter.some(
({ id: topicIdFromFilter }) => topicIdFromFilter === topicId,
),
),
),
...rest,
}));
console.log(result);
The code is working fine, but I don’t like how I’m iterating again the entries
array to filter the topics
; notice also that entries with empty topics
get filtered as well.
Is there a way to filter this array using a single iteration?
Solution:
Use Array::reduce()
to filter and map simultaneously:
const result = entries.reduce((r, entry) => {
const found = filters.find(({ id }) => entry.id === id);
if (found) {
const topics = entry.topics.filter(({ id }) => found.topics.some((topic) => topic.id === id));
topics.length && r.push({...entry, topics});
}
return r;
}, []);
console.log(result);
<script>
const entries = [
{
id: 1,
name: "Entry 1",
topics: [
{
id: 101,
name: "topic 1.1",
},
{
id: 102,
name: "topic 1.2",
},
{
id: 103,
name: "topic 1.3",
},
],
},
{
id: 2,
name: "Entry 2",
topics: [
{
id: 201,
name: "topic 2.1",
},
{
id: 202,
name: "topic 2.2",
},
{
id: 203,
name: "topic 2.3",
},
],
},
{
id: 3,
name: "Entry 3",
topics: [
{
id: 25,
name: "topic 3.1",
},
{
id: 26,
name: "topic 3.2",
},
],
},
];
const filters = [
{
id: 1,
topics: [
{
id: 101,
},
{
id: 102,
},
],
},
{
id: 2,
topics: [],
},
];
</script>