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:
check this code example: link
My Questions:
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.