简体   繁体   中英

Compare JavaScript Array of Objects to Get Min / Max With 2 Properties At the Same Time

var items = [
    {"ID": 1, "Cost": 200, "Count": 4},
    {"ID": 2, "Cost": 1000, "Count": 2},
    {"ID": 3, "Cost": 50, "Count": 10},
    {"ID": 4, "Cost": 50, "Count": 10},
    {"ID": 5, "Cost": 50, "Count": 10},
    {"ID": 6, "Cost": 50, "Count": 8}
]

I want to get the lowest Cost item, and if many of them are the same cost, I want to get the one with the highest count, and if there are still multiple, I want to get a random item.

var lowestCost = items.reduce(function(prev, curr) {
    return prev.Cost < curr.Cost ? prev : curr;
});

This is the way to get the lowest cost, any simple way to do the other comparisons simultaneously?

A not so-random solution :

 const items = [ {"ID": 1, "Cost": 200, "Count": 4}, {"ID": 2, "Cost": 1000, "Count": 2}, {"ID": 3, "Cost": 50, "Count": 10}, {"ID": 4, "Cost": 50, "Count": 10}, {"ID": 5, "Cost": 50, "Count": 10}, {"ID": 6, "Cost": 50, "Count": 8} ]; const computeLowestCost = () => { return items.reduce(function(prev, curr) { const isEqualPriceAndCount = (prev.Cost === curr.Cost) && (prev.Count === curr.Count); if (isEqualPriceAndCount) { // return a random item if price and count are the same return (!!Math.round(Math.random())) ? prev : curr; } return !!((prev.Cost < curr.Cost) || (prev.Count > curr.Count)) ? prev : curr; }); } const lowestCost = computeLowestCost(); console.log(lowestCost); // Randomness...is not that random because // 1st item has 1/2 * 1/2 * 1/2 chance to remain // 2nd item has 1/2 * 1/2 chance to remain // 3rd item has 1/2 chance to remain const results = {}; for (let i = 0; i < 1000000;i++) { const res = computeLowestCost(); results[res.ID]= !!results[res.ID] ? ++results[res.ID] : 1; } console.log(results) 

A more random solution :

 const items = [ {"ID": 1, "Cost": 200, "Count": 4}, {"ID": 2, "Cost": 1000, "Count": 2}, {"ID": 3, "Cost": 50, "Count": 10}, {"ID": 4, "Cost": 50, "Count": 10}, {"ID": 5, "Cost": 50, "Count": 10}, {"ID": 6, "Cost": 50, "Count": 8} ]; const shuffle = (array) => array.sort(() => Math.random() - 0.5); const computeLowestCost = () => { shuffle(items); return items.reduce(function(prev, curr) { const isEqualPriceAndCount = (prev.Cost === curr.Cost) && (prev.Count === curr.Count); if (isEqualPriceAndCount) { // return a random item if price and count are the same return (!!Math.round(Math.random())) ? prev : curr; } return !!((prev.Cost < curr.Cost) || (prev.Count > curr.Count)) ? prev : curr; }); } const lowestCost = computeLowestCost(); console.log(lowestCost); // Randomness...almost equally distributed const results = {}; for (let i = 0; i < 1000000;i++) { const res = computeLowestCost(); results[res.ID]= !!results[res.ID] ? ++results[res.ID] : 1; } console.log(results) // For more randomness we could shuffle the array initially 

You could get an array of the reduced data set and then take a random object.

 var items = [{ ID: 1, Cost: 200, Count: 4 }, { ID: 2, Cost: 1000, Count: 2 }, { ID: 3, Cost: 50, Count: 10 }, { ID: 4, Cost: 50, Count: 10 }, { ID: 5, Cost: 50, Count: 10 }, { ID: 6, Cost: 50, Count: 8 }], result = items.reduce((r, o) => { if (!r || r[0].Cost > o.Cost || r[0].Cost === o.Cost && r[0].Count < o.Count) { return [o]; } if (r[0].Cost === o.Cost && r[0].Count === o.Count) { r.push(o); } return r; }, null); console.log(result[Math.floor(Math.random() * result.length)]); // random console.log(result); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

So a not so simple way would be to just do another ternary right afterwards to figure out if it was equal to or greater than. And then we would have another ternary to figure out which one has the higher count etc...

var items = [
    {"ID": 1, "Cost": 200, "Count": 4},
    {"ID": 2, "Cost": 1000, "Count": 2},
    {"ID": 3, "Cost": 50, "Count": 10},
    {"ID": 4, "Cost": 50, "Count": 10},
    {"ID": 5, "Cost": 50, "Count": 10},
    {"ID": 6, "Cost": 50, "Count": 8}
]

var lowestCost = items.reduce(function(prev, curr) {
    return prev.Cost < curr.Cost ? prev: (prev.Cost == curr.Cost ? (prev.Count > curr.Count ? prev: curr) : curr);
});

Create an array to contain costs and create an array to contain count. Pick out the min value from cost and max value from the count. Filter the array and compare them. The code is self-explanatory. Comments for further explanation

 var items = [ {"ID": 1, "Cost": 200, "Count": 4}, {"ID": 2, "Cost": 1000, "Count": 2}, {"ID": 3, "Cost": 50, "Count": 10}, {"ID": 4, "Cost": 50, "Count": 10}, {"ID": 5, "Cost": 50, "Count": 10}, {"ID": 6, "Cost": 50, "Count": 8} ] let costs = items.map(objs => Number(objs["Cost"])) // get all the costs from the items let counts = items.map(objs => Number(objs["Count"])) //.. get all the counts from the items let mincost = Math.min(...costs) // get the mininum cost let maxcount = Math.max(...counts) // get the max cost let answer = items.filter((objs) => { // filter the items array if (objs["Cost"] == mincost){ // if the cost is minimum if (objs["Count"] == maxcount) { // if the count is maximum return objs // return the object } } }) if (answer.length >= 1) { // if more than one answers let random = answer[Math.floor(Math.random() * answer.length)]; // return a random value console.log(random); }else{ console.log(answer) // else just log the value } 

Math.floor(Math.random() * answer.length) this gives you a random number in the range of the length of the array. So you can just plug it into the array's selector.

This translates to

 var items = [{"ID": 1, "Cost": 200, "Count": 4},{"ID": 2, "Cost": 1000, "Count": 2},{"ID": 3, "Cost": 50, "Count": 10},{"ID": 4, "Cost": 50, "Count": 10},{"ID": 5, "Cost": 50, "Count": 10},{"ID": 6, "Cost": 50, "Count": 8}] let mincost = Math.min(...items.map(objs => Number(objs["Cost"]))) let maxcount = Math.max(...items.map(objs => Number(objs["Count"]))) let answer = items.filter(objs => objs["Cost"] == mincost && objs["Count"] == maxcount) if (answer.length >= 1) { let random = answer[Math.floor(Math.random() * answer.length)]; console.log(random); }else{ console.log(answer) } 

You can do something like this

  • Randomize array
  • Loop through randomized array, use object to hold max and min value
  • If the value is not available on min and max initialize with current value
  • Check if the price is less than or equal to min value, if it is equal and count is same keep the previous one else update with new value
  • In same way check for greater or equal than max value and update accordingly

 var items = [ {"ID": 1, "Cost": 200, "Count": 4}, {"ID": 2, "Cost": 1000, "Count": 2}, {"ID": 3, "Cost": 50, "Count": 10}, {"ID": 4, "Cost": 50, "Count": 10}, {"ID": 5, "Cost": 50, "Count": 10}, {"ID": 6, "Cost": 50, "Count": 8} ] let randomizeArray = _.shuffle(items) let op = randomizeArray.reduce((op,inp)=>{ op.max = op.max || inp op.min = op.min || inp if(inp.Cost <= op.min.Cost){ if((op.min.Cost > inp.Cost) || (op.min.Count < inp.Count)){ op.min = inp } } if(inp.Cost >= op.max.Cost){ if((op.max.Cost < inp.Cost) || (op.max.Count < inp.Count)){ op.max = inp } } return op },{}) console.log(op) 
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script> 

items = items.sort((a,b) => a.Cost < b.Cost ? -1 : (a.Cost == b.Cost ? 0 : 1))
items = items.filter(item => item.Count == items[0].Count)
return items.length != 0 ? items[0] : null

This will do it

  prev.Cost === curr.Cost && prev.Count > curr.Count || prev.Cost < curr.Cost
     ? prev
     :  curr

Or maybe more clever:

  ((prev.Cost - curr.Cost) || (prev.Count - curr.Count)) < 0 
     ? curr
     : prev

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM