【问题标题】:Filter an array of objects with nested arrays based on another array使用基于另一个数组的嵌套数组过滤对象数组
【发布时间】:2021-07-07 16:52:42
【问题描述】:

我有 2 个不同的 API。第一个返回一个事件对象数组(这个数据集正在增长并且预计会很大)。每个事件都有一个包含字符串列表的类别数组。另一个 API 返回一个过滤器对象数组。每个过滤器都有一个“名称”属性和一组关键字。在任何情况下,类别数组中包含的任何关键字都应位于此过滤器名称下。 最终目标是在屏幕上显示过滤器列表,当用户单击过滤器时,我应该在此过滤器下呈现所有事件。

事件对象示例:

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

过滤器对象示例:

{
    "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"
        }
    ]
}

经过几天的努力,我想出了这个解决方案。

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;
}

期望的输出:

  1. 要在屏幕上呈现的所有过滤器的对象或数组
  2. 以过滤器名称作为键的对象,其值将是一个事件数组,其中包含与此过滤器关键字匹配的任何关键字

查看此代码示例:link

我的问题:

  • 有没有更简单的方法来解决这个问题?
  • 我将“filteredTimelineData”对象作为初始值传递给.reduce 函数。这是合法的吗?我在网上找不到这个问题的具体答案。
  • 从时间复杂度的角度来看。如果数据集增长,此代码会导致任何内存问题吗?

【问题讨论】:

  • 你想要的输出不是很清楚。
  • 很抱歉。所需的输出是有一个对象,其过滤器名称作为键,值将是与此过滤器匹配的事件数组。
  • 能否请您在问题中添加所需的输出,这样会更清楚。
  • 做一个清晰的例子来说明过滤前后数据的样子。从你的例子来看,真的不清楚
  • 添加了一个代码示例的链接。对不起,应该在开头添加的!

标签: javascript


【解决方案1】:

这是获得上述结果的简单方法。我在这个解决方案中使用了JavaScript ES5 功能,除了 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);

【讨论】:

  • 谢谢。我已将您的答案集成到我的代码库中并对其进行了测试。肯定更简单,但是用大数据集测试时,执行时间比原来的解决方案要长!
猜你喜欢
  • 1970-01-01
  • 2016-11-17
  • 2019-10-03
  • 1970-01-01
  • 2021-08-22
  • 1970-01-01
  • 2021-11-21
  • 1970-01-01
  • 2018-04-04
相关资源
最近更新 更多