Filtering json object

Hi! I’m trying to find the best way to filter a json file. Here’s the json:

var data = {
  laureates: [
    {
      id: "1",
      firstname: "Wilhelm Conrad",
      surname: "R\u00f6ntgen",
      born: "1845-03-27",
      died: "1923-02-10",
      bornCountry: "Prussia (now Germany)",
      bornCountryCode: "DE",
      bornCity: "Lennep (now Remscheid)",
      diedCountry: "Germany",
      diedCountryCode: "DE",
      diedCity: "Munich",
      gender: "male",
      prizes: [
        {
          year: "1901",
          category: "physics",
          share: "1",
          motivation:
            '"in recognition of the extraordinary services he has rendered by the discovery of the remarkable rays subsequently named after him"',
          affiliations: [
            { name: "Munich University", city: "Munich", country: "Germany" }
          ]
        }
      ]
    },
    {
      id: "2",
     ...
     ...

I need to apply multiple filters to return only data I need. I can filter by category, year from, year until, gender, countryDied. All the filters are optional, so if a user doesn’t specify anything, it returns the full data.
What would be the best/elegant approach to this? I tried with .filter() functions, but it seems that there could be a much better way to do this.

First of all, that is a JS object, not a JSON object. Of course there isn’t much difference so we can ignore that.

I think filter is the natural choice.

const data = [
  {
    name: 'Bob',
    gender: 'male',
    age: 34,
  },
  {
    name: 'Carol',
    gender: 'female',
    age: 36,
  },
  {
    name: 'Ted',
    gender: 'male',
    age: 38,
  },
  {
    name: 'Alice',
    gender: 'female',
    age: 40,
  },
];

const arr1 = data.filter(d => d.age > 37);
console.log('arr1', arr1);

const arr2 = data.filter(d => d.gender === 'female');
console.log('arr2', arr2);

const ageAndGender = d => d.age > 37 && d.gender === 'female';

const arr3 = data.filter(ageAndGender);
console.log('arr3', arr3);

You can filter for different things and combine things like in the third example. Of course, it’s a little complicated with multiple parameters. I imagine you could create a function, and pass tests to in an array so it could be dynamic - it would filter for things that passed every test. That would be a little more flexible.

Beyond that, you’d need some library.

2 Likes

I was thinking of the problem of combining filters. I came up with this solution:

const data = [
  {
    name: 'Bob',
    gender: 'male',
    age: 34,
  },
  {
    name: 'Carol',
    gender: 'female',
    age: 36,
  },
  {
    name: 'Ted',
    gender: 'male',
    age: 38,
  },
  {
    name: 'Alice',
    gender: 'female',
    age: 40,
  },
];

const filterCombiner = (d, filterArray) => {
  for (let fn of filterArray) {
    if (!fn(d)) {
      return false;
    }
  }
  return true;
}

const filterArray1 = [
  d => d.gender === 'female',
];
const arr1 = data.filter(d => filterCombiner(d, filterArray1));
console.log('arr1', arr1);

const filterArray2 = [
  d => d.age > 37,
];
const arr2 = data.filter(d => filterCombiner(d, filterArray2));
console.log('arr2', arr2);

const filterArray3 = [
  d => d.gender === 'female',
  d => d.age > 37,
];
const arr3 = data.filter(d => filterCombiner(d, filterArray3));
console.log('arr3', arr3);

I’m sure someone else has done it before and there’s probably a nice library for it (perhaps lodash has something.)

Now that I’m thinking of it, that is an “and” filter combiner. You could also create an “or” one. And you could feed them into each other in different combinations. Hmmmm.

You can create an object with the desired filter parameters as key value pairs and use lodash filter:

_.filter(data, { key: value, ... });

https://lodash.com/docs/4.17.11#filter

1 Like

This would be perfect, but I can’t use any libaries

You couldn’t just use that out of the box anyway, because what you’re looking for has quite specific conditions, you’re not just testing whether it’s present. But you can do the same thing: you just write a wrapper function. You have specific filters to apply, so you can define everything up-front. The nested data is a slight pain, you’ll need to search in the prizes array in the condition (find I think will be useful). Something like:

function filterBy(data, filters = {}) {
  // Set up the specific defaults that will show everything:
  const defaults = {
    category: null,
    yearFrom: 1895,
    yearTo: 2100,
    gender: null
  }

  // Merge any filters with the defaults
  filters = Object.assign({}, defaults, filters);

  // Filter based on that filters object:
  return data.filter(laur => {
    return (laur.yearFrom >= filters.yearFrom) &&
           (laur.yearTo <= filters.yearTo) &&
           // and so on
  });
}
2 Likes