Reputation: 438
Suppose I have this dataset:
const data = [
{animal: 'cat', name: 'mu', year: 2016},
{animal: 'cat', name: 'muji', year: 2021},
{animal: 'cat', name: 'mine', year: 2021},
{animal: 'dog', name: 'fido', year: 2000},
{animal: 'hamster', name: 'gerry', year: 2020},
{animal: 't-rex', name: 'dino', year: 2020},
{animal: 'sheep', name: 's', year: 2019},
{animal: 'sheep', name: 'sss', year: 2016},
]
and I want to filter it by some values, for example by animal
and by year
. Very easy I can do:
const animal = 'sheep'
const year = 2019
const filteredData = data.filter(d => d.animal === animal && d.year === year)
// result
const filteredData = [
{animal: 'sheep', name: 's', year: 2019},
]
same here:
const animal = 'cat'
const year = 2021
const filteredData = [
{animal: 'cat', name: 'muji', year: 2021},
{animal: 'cat', name: 'mine', year: 2021},
]
My problem is that sometimes animal
or year
can be null
. In that case I want not to filter by that value. For example these are what I would like to obtain:
const animal = 'cat'
const year = null
const filteredData = [
{animal: 'cat', name: 'mu', year: 2016},
{animal: 'cat', name: 'muji', year: 2021},
{animal: 'cat', name: 'mine', year: 2021},
]
// ---
const animal = null
const year = 2020
const filteredData = [
{animal: 'hamster', name: 'gerry', year: 2020},
{animal: 't-rex', name: 'dino', year: 2020},
]
// ---
const animal = null
const year = null
const filteredData = [
{animal: 'cat', name: 'mu', year: 2016},
{animal: 'cat', name: 'muji', year: 2021},
{animal: 'cat', name: 'mine', year: 2021},
{animal: 'dog', name: 'fido', year: 2000},
{animal: 'hamster', name: 'gerry', year: 2020},
{animal: 't-rex', name: 'dino', year: 2020},
{animal: 'sheep', name: 's', year: 2019},
{animal: 'sheep', name: 'sss', year: 2016},
]
How can I do that?
Note that this is only a simple example, in my case I could have more than two variables as filters.
Upvotes: 1
Views: 229
Reputation: 56753
This would be my first shot at the problem (see comments in code):
const data = [{animal: 'cat', name: 'mu', year: 2016}, {animal: 'cat', name: 'muji', year: 2021}, {animal: 'cat', name: 'mine', year: 2021}, {animal: 'dog', name: 'fido', year: 2000}, {animal: 'hamster', name: 'gerry', year: 2020}, {animal: 't-rex', name: 'dino', year: 2020}, {animal: 'sheep', name: 's', year: 2019}, {animal: 'sheep', name: 'sss', year: 2016}];
// Instead of using filter literals, use a filter object:
const filter = { animal: 'dog', year: null };
// Then, for filtering, write your own filter function:
const filterAnimals = (arr, filters) => arr
.filter(animal => Object.entries(filters)
.filter(([key, value]) => value !== null) // removes filter properties equaling null
.every(([key, value]) => animal[key] === value));
console.log(filterAnimals(data, filter));
Upvotes: 0
Reputation: 13004
As you have written "I could have more than two variables as filters…", I don't think it is a good idea to hard-code filter expressions. Moreover, if both your data and filter are dynamic (if properties are not known beforehand) it is not even possible. Instead, you can use some approach like this:
const data = [
{ animal: 'cat', name: 'mu', year: 2016 },
{ animal: 'cat', name: 'muji', year: 2021 },
{ animal: 'cat', name: 'mine', year: 2021 },
{ animal: 'dog', name: 'fido', year: 2000 },
{ animal: 'hamster', name: 'gerry', year: 2020 },
{ animal: 't-rex', name: 'dino', year: 2020 },
{ animal: 'sheep', name: 's', year: 2019 },
{ animal: 'sheep', name: 'sss', year: 2016 }
];
const query = { animal: 'cat', year: null };
// remove all null values
// https://stackoverflow.com/a/38340730
const q = Object.fromEntries(Object.entries(query).filter(([_, v]) => v != null));
// https://stackoverflow.com/a/61676007
const isSubset = (superObj, subObj) => {
return Object.keys(subObj).every(ele => {
if (typeof subObj[ele] == 'object') return isSubset(superObj[ele], subObj[ele]);
return subObj[ele] === superObj[ele];
});
};
const filteredData = data.filter(d => isSubset(d, q));
console.log(filteredData);
Upvotes: 1
Reputation: 25408
You can use Nullish coalescing operator and use filters as
d.animal === (animal ?? d.animal)
const data = [
{ animal: "cat", name: "mu", year: 2016 },
{ animal: "cat", name: "muji", year: 2021 },
{ animal: "cat", name: "mine", year: 2021 },
{ animal: "dog", name: "fido", year: 2000 },
{ animal: "hamster", name: "gerry", year: 2020 },
{ animal: "t-rex", name: "dino", year: 2020 },
{ animal: "sheep", name: "s", year: 2019 },
{ animal: "sheep", name: "sss", year: 2016 },
];
const animal = null;
const year = null;
const filteredData = data.filter(
(d) => d.animal === (animal ?? d.animal) && d.year === (year ?? d.year)
);
console.log(filteredData);
Upvotes: 2