Reputation: 4735
I have a JavaScript array of objects like this:
var items = [
{
make: 'Lenovo',
config1: {
price: 388,
isCheapest: false,
},
config2: {
price: 198,
isCheapest: false,
}
config3: {
price: 176,
isCheapest: false
}
},
{
make: 'Dell',
config1: {
price: 398,
isCheapest: false,
},
config2: {
price: 336,
isCheapest: false,
}
config3: {
price: null,
isCheapest: false
}
},
{
make: 'HP',
config1: {
price: 264,
isCheapest: false,
},
config2: {
price: 198,
isCheapest: false,
}
config3: {
price: 136,
isCheapest: false
}
}
];
I need to set the isCheapest
key for each configuration in the above to true if it is the lowest value. For example, for config3, HP would be the cheapest so I would have to set HP's isCheapest
to true whereas for config2, both Lenovo and HP are the cheapest so I would have to set both their isCheapest
keys to true.
Also notice that some items can also have a price value of null. Within the application, this means that the item is not available in that specific configuration. As a result, we cannot consider items with price set as null to be the cheapest.
I tried creating 3 different loops for each config type but it just became really elaborate and sometimes inaccurate. How can I accomplish this?
Upvotes: 0
Views: 96
Reputation: 386540
A proposal with some iterating over the items and over the keys, an object for the cheapest price and some more iterating.
Edit: Better performance with smaller iteration over object obj
at the end.
var items = [{ make: 'Lenovo', config1: { price: 388, isCheapest: false, }, config2: { price: 198, isCheapest: false, }, config3: { price: 176, isCheapest: false } }, { make: 'Dell', config1: { price: 398, isCheapest: false, }, config2: { price: 336, isCheapest: false, }, config3: { price: null, isCheapest: false } }, { make: 'HP', config1: { price: 264, isCheapest: false, }, config2: { price: 198, isCheapest: false, }, config3: { price: 136, isCheapest: false } }];
void function (data) {
var obj = {};
items.forEach(function (a) {
Object.keys(a).forEach(function (k) {
if (typeof a[k] === 'object' && 'price' in a[k] && a[k].price !== null) {
if (!(k in obj) || a[k].price < obj[k].price) {
obj[k] = { price: a[k].price, items: [a[k]] };
return;
}
if (a[k].price === obj[k].price) {
obj[k].items.push(a[k]);
}
}
});
});
Object.keys(obj).forEach(function (k) {
obj[k].items.forEach(function (a) {
a.isCheapest = true;
});
});
}(items);
document.write('<pre>' + JSON.stringify(items, 0, 4) + '</pre>');
Upvotes: 2
Reputation: 376
Updated version after there was some clarification what was actually the desired result. Demo
var keys = Object.keys(items[0]).filter(function(key) {
return key != 'make'
});
keys.forEach(function(config) {
var lowestPrice = Number.MAX_SAFE_INTEGER;
// find the cheapest price of the current config
items.forEach(function(item) {
if(item[config].price && item[config].price < lowestPrice) {
lowestPrice = item[config].price;
}
});
// mark the cheapest configs
items.forEach(function(item) {
if(item[config].price && item[config].price <= lowestPrice) {
item[config].isCheapest = true;
}
});
});
Upvotes: 0
Reputation: 26867
Try this one. It collects all the configs for an item, sorts them, then marks the cheapest one.
JS
var configs = {},
configProp;
function configSorter(a, b) {
return a.config.price > b.config.price;
}
items.forEach(function (item, index) {
var prop,
rx = /^config\d+$/;
for (prop in item) {
if (item.hasOwnProperty(prop)) {
if (rx.test(prop)) {
configs[prop] = configs[prop] || [];
configs[prop].push({
item: index,
config: item[prop]
});
}
}
}
});
for (configProp in configs) {
if (configs.hasOwnProperty(configProp)) {
configs[configProp].sort(configSorter);
items[configs[configProp][0].item][configProp].isCheapest = true;
}
}
console.log(JSON.stringify(items));
Output
[{
"make": "Lenovo",
"config1": {
"price": 388,
"isCheapest": false
},
"config2": {
"price": 198,
"isCheapest": true
},
"config3": {
"price": 176,
"isCheapest": false
}
}, {
"make": "Dell",
"config1": {
"price": 398,
"isCheapest": false
},
"config2": {
"price": 336,
"isCheapest": false
},
"config3": {
"price": 764,
"isCheapest": false
}
}, {
"make": "HP",
"config1": {
"price": 264,
"isCheapest": true
},
"config2": {
"price": 198,
"isCheapest": false
},
"config3": {
"price": 136,
"isCheapest": true
}
}]
Upvotes: 0
Reputation: 214949
Well, if you cannot change the structure, you have to write some convoluted code to achieve the goal
transpose = m => m[0].map((_, c) => m.map(r => r[c]))
min = (xs, fn) => xs.reduce((min, x) => fn(min, x) > 0 ? x : min)
convert = items => {
var configs = items.map(it => Object.keys(it)
.sort()
.filter(key => key.match(/^config/))
.map(key => it[key]))
transpose(configs)
.forEach(xs =>
min(xs, (m, x) => m.price - x.price)
.isCheapest = true);
};
var items = [
{
make: 'Lenovo',
config1: {
price: 388,
isCheapest: false,
},
config2: {
price: 198,
isCheapest: false,
},
config3: {
price: 176,
isCheapest: false
}
},
{
make: 'Dell',
config1: {
price: 398,
isCheapest: false,
},
config2: {
price: 336,
isCheapest: false,
},
config3: {
price: 764,
isCheapest: false
}
},
{
make: 'HP',
config1: {
price: 264,
isCheapest: false,
},
config2: {
price: 198,
isCheapest: false,
},
config3: {
price: 136,
isCheapest: false
}
}
];
convert(items)
document.write('<pre>'+JSON.stringify(items,0,3));
Upvotes: 1