skip to Main Content

I want to filter one array of objects using another array of object, 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 that also entries with empty topics get filtered as well.

Is there a way to filter this array using one single iteration?

2

Answers


  1. How about filter and map functions:

    // to map filter topics to entry IDs.
    const map = new Map(filters.map((filter) => [filter.id, filter.topics.map((topic) => topic.id)]));
    
    const result = entries.filter((entry) => {
      const topics = map.get(entry.id);
      return topics && topics.length > 0;
    }).map((entry) => {
      const topics = map.get(entry.id);
      entry.topics = entry.topics.filter((topic) => topics.includes(topic.id));
      return entry;
    });
    
    • filter: remove entries without matching filter topics
    • map: filter topics for the remaining entries
    Login or Signup to reply.
  2. You can use reduce() to to iterate over the entries array and filter it based on the filters array.

    Example

       const result = entries.reduce((filteredEntries, entry) => {
          const matchingFilter = filters.find(filter => filter.id === entry.id);
        
          if (matchingFilter) {
            const filteredTopics = entry.topics.filter(topic =>
              matchingFilter.topics.some(filterTopic => filterTopic.id === topic.id)
            );
        
            if (filteredTopics.length > 0) {
              filteredEntries.push({ ...entry, topics: filteredTopics });
            }
          }
        
          return filteredEntries;
        }, []);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search