Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to filter array of JSON-objects with multiple integer conditions and key values

I'm struggling with a interactive search filter in VueJS (It's a app with drop downs and ranges @ codepen)

A boat has BrandName, BrandYear, Price... which I've been able to filter through using selected = {...}, but I wonder how to make the best usage of this if-statement below, to identify the Price and check min/max and return results by passing expected_selected = {...}

I'm looking for explanation/help on how I can filter a min/max value together with the following code.

Goal is to input a minimuma and maximum value together with one or more matched key values

var boats = [{
  Price: 599900,
  BrandName: "FLIPPER",
  BoatYear: 2020,
}, {
  Price: 97e3,
  BrandName: "MICORE",
  BoatYear: 2020,
}, {
  Price: 189300,
  BrandName: "LINDER",
  BoatYear: 2020,
}, {
  Price: 396900,
  BrandName: null,
  BoatYear: 2020,
}, {
  Price: 334900,
  BrandName: "MICORE",
  BoatYear: 2019,
}, {
  Price: 138700,
  BrandName: "HR",
  BoatYear: 2020,
}, {
  Price: 178900,
  BrandName: "HR",
  BoatYear: 2020,
}, {
  Price: 348900,
  BrandName: "HR",
  BoatYear: 2020,
}, {
  Price: 285800,
  BrandName: "HR",
  BoatYear: 2020,
}, {
  Price: 186900,
  BrandName: "MICORE",
  BoatYear: 2019,
}, {
  Price: 276800,
  BrandName: "MICORE",
  BoatYear: 2020,
}, {
  Price: 518900,
  BrandName: "SILVER",
  BoatYear: 2020,
}, {
  Price: 226900,
  BrandName: "MICORE",
  BoatYear: 2020,
}, {
  Price: 132600,
  BrandName: "LINDER",
  BoatYear: 2020,
}, {
  Price: 137200,
  BrandName: "LINDER",
  BoatYear: 2020,
}, {
  Price: 366900,
  BrandName: "SILVER",
  BoatYear: 2020,
}, {
  Price: 365900,
  BrandName: "SILVER",
  BoatYear: 2020,
}, {
  Price: 247900,
  BrandName: "SILVER",
  BoatYear: 2020,
}];


var selected = {
  BoatYear: 2020,
  BrandName: "LINDER"
};

var expected_selected = {
  BoatYear: 2020,
  BrandName: 'LINDER',
  Price: [0, 138000] // min , max 
}

boats = boats.filter(function(item) {
  for (var key in selected) {
    if (item[key] === undefined || item[key] != selected[key]) return false;
  }
  return true;
});

console.log(`Results: ${JSON.stringify(boats)}`);
like image 753
Jack Avatar asked Apr 29 '20 13:04

Jack


People also ask

How can I filter JSON data with multiple objects?

To filter JSON data with multiple objects, you can use the concept of filter along with ==.

How do you add multiple conditions to a filter?

If you want to put multiple conditions in filter , you can use && and || operator.

Can a JSON array have multiple types?

JSON array are ordered list of values. JSON arrays can be of multiple data types. JSON array can store string , number , boolean , object or other array inside JSON array. In JSON array, values must be separated by comma.


3 Answers

  1. Simplest solution: just hardcode all the fields

let boats = [
  {Price: 599900, BrandName: "FLIPPER", BoatYear: 2020},
  {Price: 97e3  , BrandName: "MICORE" , BoatYear: 2020},
  {Price: 189300, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 396900, BrandName: null     , BoatYear: 2020},
  {Price: 334900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 138700, BrandName: "HR"     , BoatYear: 2020},
  {Price: 178900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 348900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 285800, BrandName: "HR"     , BoatYear: 2020},
  {Price: 186900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 276800, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 518900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 226900, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 132600, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 137200, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 366900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 365900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 247900, BrandName: "SILVER" , BoatYear: 2020}
];

const expected_selected = {
  BoatYear : 2020,
  BrandName: 'LINDER',
  Price    : { min: 0, max: 138000 },
}
const filter_by = filters => item => {
  if (item.BoatYear === undefined || item.BoatYear !== filters.BoatYear ) return false
  if (item.BrandName === undefined || item.BrandName !== filters.BrandName ) return false
  if (item.Price < filters.Price.min || item.Price > filters.Price.max) return false
  return true
}
boats = boats.filter(filter_by(expected_selected))

console.log(`Results: ${JSON.stringify(boats)}`);
  1. Or use min/max everywhere

let boats = [
  {Price: 599900, BrandName: "FLIPPER", BoatYear: 2020},
  {Price: 97e3  , BrandName: "MICORE" , BoatYear: 2020},
  {Price: 189300, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 396900, BrandName: null     , BoatYear: 2020},
  {Price: 334900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 138700, BrandName: "HR"     , BoatYear: 2020},
  {Price: 178900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 348900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 285800, BrandName: "HR"     , BoatYear: 2020},
  {Price: 186900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 276800, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 518900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 226900, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 132600, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 137200, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 366900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 365900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 247900, BrandName: "SILVER" , BoatYear: 2020},
]

const expected_selected = {
  BoatYear : { min: 2020    , max: 2020     },
  BrandName: { min: 'LINDER', max: 'LINDER' },
  Price    : { min: 0       , max: 138000   },
}
const filter_by = filters => item => {
  for (var key in filters) {
    if (item[key] === undefined) return false
    if (item[key] < filters[key].min || item[key] > filters[key].max) return false
  }
  return true
}
boats = boats.filter(filter_by(expected_selected))

console.log(`Results: ${JSON.stringify(boats)}`);
  1. Or check type of selected field (in this case Array.isArray, in case of {min,max} it would be instanceof)

let boats = [
  {Price: 599900, BrandName: "FLIPPER", BoatYear: 2020},
  {Price: 97e3  , BrandName: "MICORE" , BoatYear: 2020},
  {Price: 189300, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 396900, BrandName: null     , BoatYear: 2020},
  {Price: 334900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 138700, BrandName: "HR"     , BoatYear: 2020},
  {Price: 178900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 348900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 285800, BrandName: "HR"     , BoatYear: 2020},
  {Price: 186900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 276800, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 518900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 226900, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 132600, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 137200, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 366900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 365900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 247900, BrandName: "SILVER" , BoatYear: 2020},
]

const expected_selected = {
  BoatYear : 2020,
  BrandName: 'LINDER',
  Price    : [ 0, 138000 ],
}
const filter_by = filters => item => {
  for (var key in filters) {
    if (item[key] === undefined) return false
    if (Array.isArray(filters[key])) {
      if(item[key] < filters[key][0] || item[key] > filters[key][1]) return false
    } else if (item[key] !== filters[key]) return false
  }
  return true
}
boats = boats.filter(filter_by(expected_selected))

console.log(`Results: ${JSON.stringify(boats)}`);
  1. Or better yet, use OOP

let boats = [
  {Price: 599900, BrandName: "FLIPPER", BoatYear: 2020},
  {Price: 97e3  , BrandName: "MICORE" , BoatYear: 2020},
  {Price: 189300, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 396900, BrandName: null     , BoatYear: 2020},
  {Price: 334900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 138700, BrandName: "HR"     , BoatYear: 2020},
  {Price: 178900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 348900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 285800, BrandName: "HR"     , BoatYear: 2020},
  {Price: 186900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 276800, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 518900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 226900, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 132600, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 137200, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 366900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 365900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 247900, BrandName: "SILVER" , BoatYear: 2020},
]

class MinMax {
  constructor(min, max) { this.min = min, this.max = max }
  check(val) { return val >= this.min && val <= this.max }
}
class Eq {
  constructor(val) { this.val = val }
  check(val) { return val === this.val }
}
var expected_selected = {
  BoatYear : new Eq(2020),
  BrandName: new Eq('LINDER'),
  Price    : new MinMax(0, 138000)
}
const filter_by = filters => item => {
  for (var key in filters) {
    if (item[key] === undefined) return false
    if (filters[key].check(item[key]) === false) return false
  }
  return true
}
boats = boats.filter(filter_by(expected_selected))

console.log(`Results: ${JSON.stringify(boats)}`);

This way you can extend your filters by adding new classes without changing filter_by function.

like image 198
x00 Avatar answered Oct 13 '22 20:10

x00


Checking objects using key arrays makes sense when we have many properties to validate. In your case you have 4 properties that you need to be check by equality and 3 properties that need to be checked by range, so, if you want to "Don't Repeat Yourself", you can do it using key arrays like in your example and in your project.

You can create a key array for each type you want to check and then validate all these conditions inside a single filter going through all keys. For this example, you'll have an array with keys for the values that need to be checked by equality and one array with keys for values that need to be checked by integer min/max range:

let boats = [
  {Price:599900, BrandName:"FLIPPER", BoatYear:2020},
  {Price:97000, BrandName:"MICORE", BoatYear:2020},
  {Price:189300, BrandName:"LINDER", BoatYear:2020},
  {Price:396900, BrandName:null, BoatYear:2020},
  {Price:334900, BrandName:"MICORE", BoatYear:2019},
  {Price:138700, BrandName:"HR", BoatYear:2020},
  {Price:178900, BrandName:"HR", BoatYear:2020},
  {Price:348900, BrandName:"HR", BoatYear:2020},
  {Price:285800, BrandName:"HR", BoatYear:2020},
  {Price:186900, BrandName:"MICORE", BoatYear:2019},
  {Price:276800, BrandName:"MICORE", BoatYear:2020},
  {Price:518900, BrandName:"SILVER", BoatYear:2020},
  {Price:226900, BrandName:"MICORE", BoatYear:2020},
  {Price:132600, BrandName:"LINDER", BoatYear:2020},
  {Price:137200, BrandName:"LINDER", BoatYear:2020},
  {Price:366900, BrandName:"SILVER", BoatYear:2020},
  {Price:365900, BrandName:"SILVER", BoatYear:2020},
  {Price:247900, BrandName:"SILVER", BoatYear:2020}
];

let expected = {
  BoatYear: 2020,
  BrandName: 'LINDER',
  Price: [0, 138000] // min, max 
}

// Keys that need to be checked by equality
const equals = ['BrandName', 'BoatYear', /* 'MotoBoatType', 'EngineModel' */];

// Keys that need to be checked by range
const ranges = ['Price', /* 'Width', 'Length' */]

boats = boats.filter((item) => {
  // First check the equality keys
  for (const field of equals)
    if (expected[field] && item[field] !== expected[field]) return false;
  
  // Then check the range keys
  for (const field of ranges)
    if (item[field] < expected[field][0] || item[field] > expected[field][1]) return false;

  return true;
});

console.log(`Results: ${boats.length}`, 
  boats.map(({ Price, BrandName, BoatYear }) => `${BrandName} (${BoatYear}) : ${Price}`)
);

You can even make the filter code just 2 lines using Array.prototype.every() to validate the array keys:

let boats = [
  {Price:599900, BrandName:"FLIPPER", BoatYear:2020},
  {Price:97000, BrandName:"MICORE", BoatYear:2020},
  {Price:189300, BrandName:"LINDER", BoatYear:2020},
  {Price:396900, BrandName:null, BoatYear:2020},
  {Price:334900, BrandName:"MICORE", BoatYear:2019},
  {Price:138700, BrandName:"HR", BoatYear:2020},
  {Price:178900, BrandName:"HR", BoatYear:2020},
  {Price:348900, BrandName:"HR", BoatYear:2020},
  {Price:285800, BrandName:"HR", BoatYear:2020},
  {Price:186900, BrandName:"MICORE", BoatYear:2019},
  {Price:276800, BrandName:"MICORE", BoatYear:2020},
  {Price:518900, BrandName:"SILVER", BoatYear:2020},
  {Price:226900, BrandName:"MICORE", BoatYear:2020},
  {Price:132600, BrandName:"LINDER", BoatYear:2020},
  {Price:137200, BrandName:"LINDER", BoatYear:2020},
  {Price:366900, BrandName:"SILVER", BoatYear:2020},
  {Price:365900, BrandName:"SILVER", BoatYear:2020},
  {Price:247900, BrandName:"SILVER", BoatYear:2020}
];

let expected = {
  BoatYear: 2020,
  BrandName: 'LINDER',
  Price: [0, 138000] // min, max 
}

const equals = ['BrandName', 'BoatYear', /* 'MotoBoatType', 'EngineModel' */];
const ranges = ['Price', /* 'Width', 'Length' */]

boats = boats.filter((item) => 
  equals.every(field => !expected[field] || item[field] === expected[field]) &&
  ranges.every(field => item[field] >= expected[field][0] && item[field] <= expected[field][1])
);

console.log(`Results: ${boats.length}`, 
  boats.map(({ Price, BrandName, BoatYear }) => `${BrandName} (${BoatYear}) : ${Price}`)
);

You can check this working on the fork I made from you demo project on Codepen. It has the same method and it applies the validation to all the range keys Price, Width and Length.

like image 37
Christos Lytras Avatar answered Oct 13 '22 18:10

Christos Lytras


You already might have got the solution, but just to put my thought over here I'm posting this answer. I have created a generic function which will take an object(filter object) which can have values with primitives, or object or an array. Hope this helps

let boats = [
  {Price: 599900, BrandName: "FLIPPER", BoatYear: 2020},
  {Price: 97e3  , BrandName: "MICORE" , BoatYear: 2020},
  {Price: 189300, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 396900, BrandName: null     , BoatYear: 2020},
  {Price: 334900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 138700, BrandName: "HR"     , BoatYear: 2020},
  {Price: 178900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 348900, BrandName: "HR"     , BoatYear: 2020},
  {Price: 285800, BrandName: "HR"     , BoatYear: 2020},
  {Price: 186900, BrandName: "MICORE" , BoatYear: 2019},
  {Price: 276800, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 518900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 226900, BrandName: "MICORE" , BoatYear: 2020},
  {Price: 132600, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 137200, BrandName: "LINDER" , BoatYear: 2020},
  {Price: 366900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 365900, BrandName: "SILVER" , BoatYear: 2020},
  {Price: 247900, BrandName: "SILVER" , BoatYear: 2020}
];

var expected_selected = {
  BoatYear: 2020,
  BrandName: 'LINDER',
  Price: [0, 138000] // min , max 
}

var expected_selected_2 = {
  BoatYear: 2020,
  BrandName: 'LINDER',
  Price: {min:0, max:138000} 
}

var expected_selected_3 = {
  BoatYear: 2020,
  BrandName: 'LINDER',
  Price: 137200
}

const filter_by = (filter) => item => {
for(let key in filter) {
  if(filter.hasOwnProperty(key)){
      if(Array.isArray(filter[key])) {
        if(!item[key] || item[key] < filter[key][0] || item[key] > filter[key][1]) return false;
      }
      else if(typeof filter[key] === "object") {
        if(!item[key] || item[key] < filter[key]["min"] || item[key] > filter[key]["max"]) return false;
      }
      else {
        if(!item[key] || item[key] !== filter[key]) return false;
      }
    } else {
      return false
    }
  }
  return true
}

const results = boats.filter(filter_by(expected_selected))
console.log(`Results: ${JSON.stringify(results, null, 2)}`);

const results_2 = boats.filter(filter_by(expected_selected_2))
console.log(`Results_2: ${JSON.stringify(results_2, null, 2)}`);

const results_3 = boats.filter(filter_by(expected_selected_3))
console.log(`Results_3: ${JSON.stringify(results_3, null, 2)}`);
like image 2
Nithish Avatar answered Oct 13 '22 19:10

Nithish