比较对象的 JavaScript 数组以获得最小/最大值

我有一个对象数组,我想比较这些对象在一个特定的对象属性。这是我的数组:

var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]

我想集中在“成本”上,并得到一个最小值和最大值。我意识到我只需要获取成本值,然后把它们放到一个 javascript 数组中,然后运行 快速 JavaScript 最大/最小

然而,是否有一种更简单的方法来做到这一点,即绕过中间的数组步骤,直接关闭对象属性(在本例中为“ Cost”) ?

159359 次浏览

如果您不关心正在修改的数组,请使用 sort

myArray.sort(function (a, b) {
return a.Cost - b.Cost
})


var min = myArray[0],
max = myArray[myArray.length - 1]

一种方法是遍历所有元素并将其与最高/最低值进行比较。

(创建一个数组,调用数组方法对于这个简单的操作来说是多余的)。

 // There's no real number bigger than plus Infinity
var lowest = Number.POSITIVE_INFINITY;
var highest = Number.NEGATIVE_INFINITY;
var tmp;
for (var i=myArray.length-1; i>=0; i--) {
tmp = myArray[i].Cost;
if (tmp < lowest) lowest = tmp;
if (tmp > highest) highest = tmp;
}
console.log(highest, lowest);

我认为 Rob W 的回答确实是正确的(+ 1) ,但只是为了好玩: 如果你想变得“聪明”,你 可以做这样的事情:

var myArray =
[
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]


function finder(cmp, arr, attr) {
var val = arr[0][attr];
for(var i=1;i<arr.length;i++) {
val = cmp(val, arr[i][attr])
}
return val;
}


alert(finder(Math.max, myArray, "Cost"));
alert(finder(Math.min, myArray, "Cost"));

或者,如果你有一个深度嵌套的结构,你可以得到更多的功能,并做以下事情:

var myArray =
[
{"ID": 1, "Cost": { "Wholesale":200, Retail: 250 }},
{"ID": 2, "Cost": { "Wholesale":1000, Retail: 1010 }},
{"ID": 3, "Cost": { "Wholesale":50, Retail: 300 }},
{"ID": 4, "Cost": { "Wholesale":500, Retail: 1050 }}
]


function finder(cmp, arr, getter) {
var val = getter(arr[0]);
for(var i=1;i<arr.length;i++) {
val = cmp(val, getter(arr[i]))
}
return val;
}


alert(finder(Math.max, myArray, function(x) { return x.Cost.Wholesale; }));
alert(finder(Math.min, myArray, function(x) { return x.Cost.Retail; }));

这些可以很容易地被咖喱成更有用/更具体的形式。

另一个答案与肯纳贝克的答案相似,但都在一条线上:

maxsort = myArray.slice(0).sort(function (a, b) { return b.ID - a.ID })[0].ID;

您可以使用内置 Array 对象来代替使用 Math.max/Math.min:

var arr = [1,4,2,6,88,22,344];


var max = Math.max.apply(Math, arr);// return 344
var min = Math.min.apply(Math, arr);// return 1

这是更好的解决方案

    var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
var lowestNumber = myArray[0].Cost;
var highestNumber = myArray[0].Cost;


myArray.forEach(function (keyValue, index, myArray) {
if(index > 0) {
if(keyValue.Cost < lowestNumber){
lowestNumber = keyValue.Cost;
}
if(keyValue.Cost > highestNumber) {
highestNumber = keyValue.Cost;
}
}
});
console.log('lowest number' , lowestNumber);
console.log('highest Number' , highestNumber);

Reduce 对类似这样的事情很有用: 对对象数组执行聚合操作(如 min、 max、 avg 等) ,并返回单个结果:

myArray.reduce(function(prev, curr) {
return prev.Cost < curr.Cost ? prev : curr;
});

或者你可以用 ES6函数语法定义内部函数:

(prev, curr) => prev.Cost < curr.Cost ? prev : curr

如果你想变得可爱,你可以把这个附加到数组:

Array.prototype.hasMin = function(attrib) {
return (this.length && this.reduce(function(prev, curr){
return prev[attrib] < curr[attrib] ? prev : curr;
})) || null;
}

现在你可以说:

myArray.hasMin('ID')  // result:  {"ID": 1, "Cost": 200}
myArray.hasMin('Cost')    // result: {"ID": 3, "Cost": 50}
myEmptyArray.hasMin('ID')   // result: null

请注意,如果您打算使用这一点,它并没有完全检查每一种情况。如果传入一个基元类型的数组,它将失败。如果检查不存在的属性,或者不是所有对象都包含该属性,则将获得最后一个元素。这个版本有点笨重,但有那些支票:

Array.prototype.hasMin = function(attrib) {
const checker = (o, i) => typeof(o) === 'object' && o[i]
return (this.length && this.reduce(function(prev, curr){
const prevOk = checker(prev, attrib);
const currOk = checker(curr, attrib);
if (!prevOk && !currOk) return {};
if (!prevOk) return curr;
if (!currOk) return prev;
return prev[attrib] < curr[attrib] ? prev : curr;
})) || null;
}

使用 Math函数并从 map中取出所需的值。

下面是 jsbin:

Https://jsbin.com/necosu/1/edit?js,console

var myArray = [{
"ID": 1,
"Cost": 200
}, {
"ID": 2,
"Cost": 1000
}, {
"ID": 3,
"Cost": 50
}, {
"ID": 4,
"Cost": 500
}],


min = Math.min.apply(null, myArray.map(function(item) {
return item.Cost;
})),
max = Math.max.apply(null, myArray.map(function(item) {
return item.Cost;
}));


console.log('min', min);//50
console.log('max', max);//1000

更新:

如果你想使用 ES6:

var min = Math.min.apply(null, myArray.map(item => item.Cost)),
max = Math.max.apply(null, myArray.map(item => item.Cost));

使用 Prototype.reduce (),您可以插入比较器函数来确定数组中的 min、 max 等项。

var items = [
{ name : 'Apple',  count : 3  },
{ name : 'Banana', count : 10 },
{ name : 'Orange', count : 2  },
{ name : 'Mango',  count : 8  }
];


function findBy(arr, key, comparatorFn) {
return arr.reduce(function(prev, curr, index, arr) {
return comparatorFn.call(arr, prev[key], curr[key]) ? prev : curr;
});
}


function minComp(prev, curr) {
return prev < curr;
}


function maxComp(prev, curr) {
return prev > curr;
}


document.body.innerHTML  = 'Min: ' + findBy(items, 'count', minComp).name + '<br />';
document.body.innerHTML += 'Max: ' + findBy(items, 'count', maxComp).name;

添加到 Tristan Reid 的回答中(+ 使用 es6) ,您可以创建一个接受回调的函数,该函数将包含您希望应用到 prevcurr的操作符:

const compare = (arr, key, callback) => arr.reduce((prev, curr) =>
(callback(prev[key], curr[key]) ? prev : curr), {})[key];


// remove `[key]` to return the whole object

然后你可以简单地称之为:

const costMin = compare(myArray, 'Cost', (a, b) => a < b);
const costMax = compare(myArray, 'Cost', (a, b) => a > b);

这可以通过 loash 的 minBymaxBy函数来实现。

Lodash 的 minBymaxBy文档

_.minBy(array, [iteratee=_.identity])

_.maxBy(array, [iteratee=_.identity])

中的每个元素调用的迭代器 数组生成对值进行排序的条件 Iteratee 通过一个参数调用: (value)。

解决方案

var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]


const minimumCostItem = _.minBy(myArray, "Cost");


console.log("Minimum cost item: ", minimumCostItem);


// Getting the maximum using a functional iteratee
const maximumCostItem = _.maxBy(myArray, function(entry) {
return entry["Cost"];
});


console.log("Maximum cost item: ", maximumCostItem);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

尝试(a是数组,f是字段进行比较)

let max= (a,f)=> a.reduce((m,x)=> m[f]>x[f] ? m:x);
let min= (a,f)=> a.reduce((m,x)=> m[f]<x[f] ? m:x);

let max= (a,f)=> a.reduce((m,x)=> m[f]>x[f] ? m:x);
let min= (a,f)=> a.reduce((m,x)=> m[f]<x[f] ? m:x);


// TEST


var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]


console.log('Max Cost', max(myArray, 'Cost'));
console.log('Min Cost', min(myArray, 'Cost'));


console.log('Max ID', max(myArray, 'ID'));
console.log('Min ID', min(myArray, 'ID'));

为了麦克斯

Math.max.apply(Math, myArray.map(a => a.Cost));

为了我

Math.min.apply(Math, myArray.map(a => a.Cost));

我们可以用两种方法来解决问题 上面已经解释了这两种方法,但是缺少性能测试,因此完成了这个测试

1,本地 Java 脚本方式
2、先排序对象,然后容易得到最小值 来自排序的 obj 的 max

我还测试了两种方法的性能

您还可以运行和测试性能... 快乐编码(:

//first approach


var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]


var t1 = performance.now();;


let max=Math.max.apply(Math, myArray.map(i=>i.Cost))


let min=Math.min.apply(Math, myArray.map(i=>i.Cost))


var t2   = performance.now();;


console.log("native fuction took " + (t2 - t1) + " milliseconds.");


console.log("max Val:"+max)
console.log("min Val:"+min)


//  Second approach:




function sortFunc (a, b) {
return a.Cost - b.Cost
}


var s1 = performance.now();;
sortedArray=myArray.sort(sortFunc)




var minBySortArray = sortedArray[0],
maxBySortArray = sortedArray[myArray.length - 1]
    

var s2   = performance.now();;
console.log("sort funciton took  " + (s2 - s1) + " milliseconds.");
console.log("max ValBySortArray :"+max)
console.log("min Val BySortArray:"+min)

使用 Math.minMath.max:

var myArray = [
{ id: 1, cost: 200},
{ id: 2, cost: 1000},
{ id: 3, cost: 50},
{ id: 4, cost: 500}
]




var min = Math.min(...myArray.map(item => item.cost));
var max = Math.max(...myArray.map(item => item.cost));


console.log("min: " + min);
console.log("max: " + max);

对于一个简洁、现代的解决方案,可以对数组执行 reduce操作,跟踪当前的最小值和最大值,因此数组只迭代一次(这是最佳的)。

let [min, max] = myArray.reduce(([prevMin,prevMax], {Cost})=>
[Math.min(prevMin, Cost), Math.max(prevMax, Cost)], [Infinity, -Infinity]);

演示:

var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
let [min, max] = myArray.reduce(([prevMin,prevMax], {Cost})=>
[Math.min(prevMin, Cost), Math.max(prevMax, Cost)], [Infinity, -Infinity]);
console.log("Min cost:", min);
console.log("Max cost:", max);

max = totalAVG.reduce(function (a, b) { return Math.max(a, b)}, -Infinity);


min = totalAVG.reduce(function (a, b) {return Math.min(a, b)}, Infinity);