1

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.

3
  • 4
    Simple solution would be to just check for it: data.filter(d => animal != null ? d.animal === animal : true && year != null ? d.year === year : true) Commented May 5, 2021 at 8:01
  • 4
    or instead of using ternary expressions you could just add an or statement (!year || d.year === year) Commented May 5, 2021 at 8:02
  • It would be better to use Nullish coalescing operator Commented May 5, 2021 at 8:24

3 Answers 3

2

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

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.