this is not a duplicate. please see my ment below!
does somebody knows an more efficient solution than for loops in ES6?
I have written the following, which lacks of performance. Any improvement ideas? Highly appreciated.
Basically i have an object regarding cars and an array about user preferences. Expected behavior is to push all relevant car names into an array.
Users can give any amount of preferences. A car name should be only pushed, if ALL specifications are mentioned in preferences. Therefore some preferences will be "leftovers".
For that reason in the following example Honda appears, but not BMW, which is the expected (but very slow behavior).
// Car objects
const cars = [{
name: "Honda",
category: "eco",
specs: {
0: "green",
1: "fast",
2: "automatic"
}
},
{
name: "BMW",
category: "sport",
specs: {
0: "blue",
1: "fast",
2: "automatic"
}
}
]
// User preferences
const preferences = ["green", "fast", "4x4", "automatic", "panorama"]
// function to get length/amount of car specifications
function objsize(Myobj) {
var osize = 0,
key;
for (key in Myobj) {
if (Myobj.hasOwnProperty(key)) osize++;
}
return Object(osize);
};
//function to check if ALL specifications are included in the user preferences
function checkSpecs(spec_item) {
return preferences.includes(spec_item)
}
// main function
function filter_func() {
//final results
let matched_cars = []
for (i = 0; i < objsize(cars); i++) {
let specs_collector = []
for (j = 0; j < objsize(cars[i].specs); j++) {
specs_collector.push(cars[i].specs[j])
}
if (specs_collector.every(checkSpecs) === true) {
matched_cars.push(cars[i].name)
specs_collector = []
}
}
console.log(matched_cars)
}
filter_func()
this is not a duplicate. please see my ment below!
does somebody knows an more efficient solution than for loops in ES6?
I have written the following, which lacks of performance. Any improvement ideas? Highly appreciated.
Basically i have an object regarding cars and an array about user preferences. Expected behavior is to push all relevant car names into an array.
Users can give any amount of preferences. A car name should be only pushed, if ALL specifications are mentioned in preferences. Therefore some preferences will be "leftovers".
For that reason in the following example Honda appears, but not BMW, which is the expected (but very slow behavior).
// Car objects
const cars = [{
name: "Honda",
category: "eco",
specs: {
0: "green",
1: "fast",
2: "automatic"
}
},
{
name: "BMW",
category: "sport",
specs: {
0: "blue",
1: "fast",
2: "automatic"
}
}
]
// User preferences
const preferences = ["green", "fast", "4x4", "automatic", "panorama"]
// function to get length/amount of car specifications
function objsize(Myobj) {
var osize = 0,
key;
for (key in Myobj) {
if (Myobj.hasOwnProperty(key)) osize++;
}
return Object(osize);
};
//function to check if ALL specifications are included in the user preferences
function checkSpecs(spec_item) {
return preferences.includes(spec_item)
}
// main function
function filter_func() {
//final results
let matched_cars = []
for (i = 0; i < objsize(cars); i++) {
let specs_collector = []
for (j = 0; j < objsize(cars[i].specs); j++) {
specs_collector.push(cars[i].specs[j])
}
if (specs_collector.every(checkSpecs) === true) {
matched_cars.push(cars[i].name)
specs_collector = []
}
}
console.log(matched_cars)
}
filter_func()
objsize
is unnecessary and wrong. Object(osize)
creates an a Number object, which means that 5 === Object(5)
is false
. To iterate over an array, use for(var i = 0; i < array.length; i++)
or for (var item of arr)
or arr.forEach(function(item) {})
). To iterate over an object, use for (var prop in obj)
. But it looks like cars[i].specs
should really be an array, not an object.
– Felix Kling
Commented
Oct 16, 2018 at 23:54
for
loops are heavily optimized in browsers. Of course a loop might not be the "best" solution for a problem, but that's a different issue.
– Felix Kling
Commented
Oct 16, 2018 at 23:56
You can't really avoid looking at every car and you can't avoid looking at every spec in the car because you want to test each of those. You can avoid looping over the preferences every time by using a Set.
So this may or may not be faster, but it's much simpler and much easier to understand because the code almost reads like English: filter cars where every spec is in the preferences:
// Car objects
const cars = [{
name: "Honda",
category: "eco",
specs: ["green", "fast","automatic"]
},
{
name: "BMW",
category: "sport",
specs: ["blue", "fast","automatic"]
}
]
const preferences = new Set(["green", "fast", "4x4", "automatic", "panorama"])
let filtered = cars.filter(car => car.specs.every(spec => preferences.has(spec)))
console.log(filtered)
-- EDIT --
Using the data in the OP:
const array_intersect = (a, b) => a.filter( i => (b.indexOf(i) >= 0) )
const a_contains_b = (a, b) => array_intersect(a, b).length == b.length
var cars = [{
name: "Honda",
category: "eco",
specs: ["green", "fast", "automatic"]
},
{
name: "BMW",
category: "sport",
specs: ["blue", "fast", "automatic"]
}
]
const preferences = ["green", "fast", "4x4", "automatic", "panorama"]
let filtered = cars.filter(car => a_contains_b(preferences, car.specs))
console.log(filtered);
There is no way to escape at least one loop. You always have to loop through all the cars, be it with a for... or with another construct like array.filter(). But there is another way to gain performance. You can use bitmasks. This would require changing the data structure of the car object so that each car already contains the bitmask corresponding to its specs, and when the user chooses the desired specs, likewise the spec codes should be added. (However, I suspect this might be too much hassle for little gain.)
// Let's pretend there are preset binary digits corresponding
// to each one of the available preferences:
//
// "blue" => 1
// "green" => 2
// "red" => 4
// "fast" => 8
// "slow" => 16
// "automatic" => 32
// "4x4" => 64
// "panorama" => 128
//
// You would encode this into the data before processing
var cars = [{
name: "Honda",
category: "eco",
specs: ["green", "fast", "automatic"],
bin_specs: 42 // 2 + 8 + 32
},
{
name: "BMW",
category: "sport",
specs: ["blue", "fast", "automatic"],
bin_specs: 41 // 1 + 8 + 32
}
]
const preferences = ["green", "fast", "4x4", "automatic", "panorama"]
const bin_preferences = 234 // 2 + 8 + 64 + 32 + 128]
let filtered = cars.filter(car => (car.bin_specs & bin_preferences) === car.bin_specs)
console.log(filtered);