I have a dict of a dict that looks something like this:
{
"Chain1": {
Country1: [
{name: "a", id: "1"},
{name: "b", id: "2"}
]
},
"Chain2": {
Country1: [
{name: "c", id: "3"},
{name: "d", id: "4"}
],
Country2: [
{name: "e", id: "5"},
{name: "f", id: "6"}
]
}
}
I would like to be able to filter on chain, country and name. But I'm a bit unsure how to filter further down than chain level. So far I've tried
const filteredChains = Object.entries(dict).filter(
([chain, countries]) => {
if (searchQuery.length > 0) {
return chain.includes(searchQuery);
} else {
return chain;
}
}
);
searchQuery
is the input from a search bar that allows the user to filter what is being displayed. The user should be able to filter on chain, country and name. If the user types "j" I would expect the outcome to be that keys or values containing "j" only is shown.
Is it possible to expand on this one to filter further down? Or am I totally off?
CodePudding user response:
reduce
over the object entries and for each key/value push them into a temporary array, and then merge that array with the accumulator for the next iteration.
const dict={Chain1:{Country1:[{name:"a",id:"1"},{name:"b",id:"2"}]},Chain2:{Country1:[{name:"c",id:"3"},{name:"d",id:"4"}],Country2:[{name:"e",id:"5"},{name:"f",id:"6"}]}};
function search(searchQuery) {
// Making sure that we match against lowercase letters
searchQuery = searchQuery.toLowerCase();
if (searchQuery.length > 0) {
// `reduce` over the object entries
return Object.entries(dict).reduce((acc, [chain, country]) => {
const temp = [];
// If the chain key includes the query,
// push it into the temp array
if (chain.toLowerCase().includes(searchQuery)) {
temp.push(chain);
}
const countryKey = Object.keys(country)[0];
// If the country key includes the query,
// push it into the temp array
if (countryKey.toLowerCase().includes(searchQuery)) {
temp.push(countryKey);
}
const names = Object.values(country)[0].map(obj => obj.name);
// `filter` out the objects under country that
// match the query and... push them into the temp array
const filteredNames = names.filter(name => {
return name.toLowerCase().includes(searchQuery);
});
temp.push(filteredNames);
return [...acc, ...temp.flat()];
}, []);
}
return null;
}
console.log(search('a'));
console.log(search('i'));
console.log(search('c'));
console.log(search(''));
CodePudding user response:
I assume, that you want the same object back, with only the countries in it, which collide with the search query by name or id.
var dict = {
Chain1: {
Country1: [
{ name: 'a', id: '1' },
{ name: 'b', id: '2' },
],
},
Chain2: {
Country1: [
{ name: 'c', id: '3' },
{ name: 'd', id: '4' },
],
Country2: [
{ name: 'e', id: '5' },
{ name: 'f', id: '6' },
],
},
};
function filter(dict, searchQuery){
if(!dict) return null;
if(!searchQuery || searchQuery.length <=0) return dict;
const result = {};
for (chainKey in dict) {
if (dict.hasOwnProperty(chainKey)) {
const chains = dict[chainKey];
for (countryKey in chains) {
if (chains.hasOwnProperty(countryKey)) {
const countries = chains[countryKey];
const filteredCountries = countries.filter(
(country) =>
country.name.includes(searchQuery) ||
country.id.includes(searchQuery)
);
if (filteredCountries.length) {
result[chainKey] = {};
result[chainKey][countryKey] = filteredCountries;
}
}
}
}
}
return result;
}
console.log(filter(dict, "a"));
console.log(filter(dict, "e"));
console.log(filter(dict, "f"));