简体   繁体   中英

Filter an array of objects with nested arrays based on another array

I've 2 different APIs. first one returns an array of event objects (this data set is growing and expected to be large). each event has a category array that has a list of strings. The other API returns an array of filter objects. each filter has a "name" property and an array of keywords. any keyword included in the categories array in any event should go under this filter name. The ultimate goal is to have a list of filters on the screen and when a user click on a filter I should render all events under this filter.

Event Object Example:

{
            "text": {
                "headline": "Headline example",
                "text": "event description "
            },
            "media": {
                "url": "https://www.google.com/",
                "caption": "",
                "credit": ""
            },
            "categories": [
                "National",
                "Canada",
                "British Columbia"
            ]
        }

Filters Object Example:

{
    "filters": [
        {
            "keywords": [
                "Atlantic",
                "New Brunswick",
                "Newfoundland and Labrador",
                "Prince Edward Island",
                "Nova Scotia"
            ],
            "name": "Atlantic"
        },
        {
            "keywords": [
                "ontario",
                "Quebec"
            ],
            "name": "Central Canada"
        },
        {
            "keywords": [
                "Manitoba",
                "Saskatchewan",
                "Alberta"
            ],
            "name": "Prairie Provinces"
        },
        {
            "keywords": [
                "British Columbia"
            ],
            "name": "West Coast"
        },
        {
            "keywords": [
                "Nunavut",
                "Northwest Territories",
                "Yukon Territory"
            ],
            "name": "North"
        },
        {
            "keywords": [
                "National"
            ],
            "name": "National"
        }
    ]
}

After a couple of days working on it I came up with this solution.

function filterTimelineData(filtersObj, timelineData) {
  if (!timelineData || !filtersObj) return [];
  // create a new object with filters "name" as key;
  const filters = Object.keys(filtersObj);
  const filteredTimelineData = Object.keys(filtersObj).reduce((o, key) => ({ ...o, [key]: [] }), {});

  const filteredData = timelineData.events.reduce((acc, current) => {

    let filterMatch = false;
    let filterMatchName = '';
    for (let i = 0; i < filters.length; i++) {
      filterMatch = current.categories.some(item => {
        return filtersObj[filters[i]].includes(item.toLocaleLowerCase());
      });
      if (filterMatch && filterMatchName !== filters[i]) { // to avoid duplicated items with different categories under the same filter
        filterMatchName = filters[i];
        acc[filters[i]].push(current);
      }
    }
    return acc;
  }, filteredTimelineData);

  return filteredData;
}

export function timelineFiltersObj(filters) {
  const filtersObj = filters.filters.reduce((acc, current) => {
    const filterName = current.name.replace(/ /g, '_').toLocaleLowerCase();
    if (!acc.hasOwnProperty(filterName)) {
      acc[filterName] = [];
    }
    acc[filterName] = [].concat(current.keywords.map(item => item.toLocaleLowerCase()));
    return acc;
  }, {});
  return filtersObj;
}

Desired output:

  1. An object or an array for all filters to be rendered on the screen
  2. An object with filters name as a key and the value would be an array of events that has any keyword that matches any of this filter keywords

check this code example: link

My Questions:

  • Is there an easier/simpler way to solve this problem?
  • I'm passing "filteredTimelineData" object as initial value to.reduce function. Is this legitimate? I couldn't find any answers online to this question specifically.
  • from a time complexity prospective. will this code cause any memory issue if the dataset grows?

This is a simple way to get the above result. I am using JavaScript ES5 features in this solution which is supported by almost all the browsers except IE9

 const filters = { "filters": [ { "keywords": [ "Atlantic", "New Brunswick", "Newfoundland and Labrador", "Prince Edward Island", "Nova Scotia" ], "name": "Atlantic" }, { "keywords": [ "ontario", "Quebec" ], "name": "Central Canada" }, { "keywords": [ "Manitoba", "Saskatchewan", "Alberta" ], "name": "Prairie Provinces" }, { "keywords": [ "British Columbia" ], "name": "West Coast" }, { "keywords": [ "Nunavut", "Northwest Territories", "Yukon Territory" ], "name": "North" }, { "keywords": [ "National" ], "name": "National" } ] }; const timelineData = { "events": [ { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "" }, "categories": [ "New Brunswick" ] }, { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "" }, "categories": [ "National" ] }, { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "https://youtu.be/poOO4GN3TN4" }, "categories": [ "Northwest Territories" ] }, { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "" }, "categories": [ "Ontario" ] }, { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "" }, "categories": [ "National" ] }, { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "https://philanthropy.cdn.redcross.ca/timeline/July2020-3.jpg" }, "categories": [ "British Columbia" ] }, { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "" }, "categories": [ "Alberta" ] }, { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "" }, "categories": [ "Prince Edward Island" ] }, { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "" }, "categories": [ "National" ] }, { "text": { "headline": "headline example", "text": "event-descriprion" }, "media": { "url": "" }, "categories": [ "National" ] } ] }; var categoriesToEventsMap = timelineData.events.reduce((res, event) => { event.categories.forEach(c=> { res = {...res, [c.toLowerCase()]: [...(res[c.toLowerCase()] || []), event] } }); return res; }, {}) var result = filters.filters.reduce((acc, filter) => { let events = [] const filterName = filter.name.replace(' ', '_').toLowerCase(); filter.keywords.forEach((key)=>{ events = [...events, ...(categoriesToEventsMap[key.toLowerCase()] || [])]; }); acc[filterName] = [...(acc[filterName] || []), ...events] return acc; }, {}); console.log(result);

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