JackH
JackH

Reputation: 4735

How do I get the lowest value from an array of objects and its index in JavaScript?

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

Answers (4)

Nina Scholz
Nina Scholz

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

Jarno Lonardi
Jarno Lonardi

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

zero298
zero298

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

georg
georg

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

Related Questions