How to write a system for sorting data by parameters so that it scales well regardless of the number of various parameters? For example, there is a conditional json-format in which data is stored:

{ "city": "Москва", "population": "12", "standart_of_living": "9.5", "per_capita_income": "50000", "trust_in_government": "9.8" } 

If you write code in JavaScript, you get a static loop without the ability to dynamically add / remove parameters. And if I wrote a simple static sample, I would do this:

 for(let i = 0; i < json.length; i++) { if(json[i].population >= 10 && json[i].standart_of_living >= 8) $('#some_div').append("Город: " + json[i].city); } 

The result of the cycle would be the conclusion Город: Москва . But what if the user on the page wants to choose cities by the parameter "Trust level in government" (trust_in_government)? My code is static and I cannot dynamically add the && json[i].trust_in_government >= 9 parameter to it. Is there an algorithm that allows sorting by parameters without having to scribble through mountains of code?

    1 answer 1

    There is a filter method that allows you to reduce the code you write to a single line:

     json.filter(i => i.population>9 && i.standart_of_living>7).forEach(i => $('#some_div').append(`Город: ${i.city}`)); 

    Using the composition and the every method, it is easy to use it to filter by any parameters:

     let rules = [ {prop: 'population', val: 9}, {prop: 'standart_of_living', val: 7}, ]; json.filter(i => rules.every(rule => i[rule.prop]>rule.val)) 

    The only problem is that you cannot put a comparison operator in the array of rules. But you can put it there as a string, and use it with an eval , a Function constructor, or a trivial switch, like this:

     let rules = [ {prop: 'population', val: 9, op: '=='}, {prop: 'standart_of_living', val: 7, op: '>'}, ]; // Вариант с eval самый короткий, если бы я делал не для примера, я бы использовал switch: json.filter(i => rules.every(rule => eval(`${i[rule.prop]}${rule.op}${rule.val}`))) 

    Now, instead of scribbling mountains of code, for each new filtering rule you need to add three parameters to the array of rules - what to compare, how to compare, what to compare with.

     const json = [ {a:1, b:2, c: 'Test'}, {a:1, b:20, c: 'Hello world'}, {a:1, b:-2, c: 'Test'}, {a:1, b:'2', c: 'Hello world'}, {a:'Hello world', c: 'Test'}, {a:1, b:[], c: 'Test'} ]; // Вариант с switch: const filter = rules => json.filter(i => rules.every(rule => { switch (rule.op){ case "===": return i[rule.prop] === rule.val; case "==": return i[rule.prop] == rule.val; case "!=": return i[rule.prop] != rule.val; case "!==": return i[rule.prop] !== rule.val; case ">": return i[rule.prop] > rule.val; case "<": return i[rule.prop] < rule.val; case "<=": return i[rule.prop] <= rule.val; case ">=": return i[rule.prop] >= rule.val; default: return false; } })); console.log(filter([{prop: 'a', val: 1, op: '!='}])); console.log(filter([{prop: 'b', val: 0, op: '>'}])); console.log(filter([{prop: 'b', val: undefined, op: '==='}])); 

    • Great, thank you! That is, to add a new rule, I can, for example, bind to the checkbox. And when the checkbox is checked, call the push() method and insert a new rule into the array of rules? - JamesJGoodwin
    • @JamesJGoodwin as an option. You can modify this algorithm for training so that it checks the nested fields) - Darth
    • Hello again. Over time, the logic in my application changed and the arrays became objects. Now I have both rules - an object, and json - an object. Could you describe the application logic so that I can rewrite the algorithm myself? Now I understand that it works like this - there are two cycles, one is nested in the other (every nested in filter). In switch checked if the operation matches what is specified in rule.op and returns a boolean value. If true, then the json array element is pushed into a temporary array. If false, no. - JamesJGoodwin
    • I tried to rewrite it for myself - nothing happened. I get an empty object. - JamesJGoodwin