オブジェクトのJavaScript配列を比較して最小/最大を取得する


95

オブジェクトの配列があり、それらのオブジェクトを特定のオブジェクトプロパティで比較したい。これが私の配列です:

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

具体的には「コスト」に焦点を当てて、最小値と最大値を取得します。コストの値を取得してJavaScript配列にプッシュしてから、Fast JavaScript Max / Minを実行できることを理解しています。

しかし、途中で配列のステップをバイパスし、オブジェクトのプロパティ(この場合は「コスト」)を直接オフにすることで、これを行う簡単な方法はありますか?

回答:


53

この場合の最速の方法は、すべての要素をループして、それまでの最高値/最低値と比較することです。

(配列の作成、配列メソッドの呼び出しは、この単純な操作には過剰です)。

 // 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);

これは理にかなっています。私は、配列内のデータを外部の高い/低い数値ではなく相互に比較することを考え続けてきました。
firedrawndagger 2012年

1
私が変更する唯一のことは、最低と最高を設定することは少し冗長です。私は、ループ1少ない時間とセットではなくだろうlowest=highest=myArray[0]し、その後1でループを開始
J.ホームズ

1
@ 32bitkid良い点。であるべきmyArray[0].Costです。ただし、最初の要素がない場合は、エラーがスローされます。そのため、追加のチェックが必要です。小さなパフォーマンスの向上を取り消す可能性があります。
Rob W

オブジェクト自体を返して欲しいので、ここに来ました。最低値ではなく、それは簡単でした。助言がありますか?
Wilt

1
@Wiltはい、最低値を見つけたときに更新される別の変数を維持します。つまりvar lowestObject; for (...)if (tmp < lowest) { lowestObject = myArray[i]; lowest = tmp; }
Rob W

159

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;
 }

13
私の意見では最良の回答です。配列は変更されず、「配列の作成、配列メソッドの呼び出しはこの単純な操作には過剰です」という答えよりもはるかに簡潔です
Julian Mann

これは、大規模なデータセット(30以上の列/ 10万行)のパフォーマンスに対する絶対最良の回答です。
2016年

2
不思議なことに、reduceチェックを実行すると、配列の最初の要素prev.Costが未定義にならないのですか?それとも0として開始しますか?
GrayedFox 2017年

1
良い点@Saheb。編集したので、その場合はnullが返されます。
Tristan Reid、

1
非オブジェクトなどの問題を引き起こす可能性のある他の入力、または一部のオブジェクトにそのプロパティが欠落していないかどうかのチェックも追加しました。結局のところ、私が思うにほとんどの場合それは少し強引になっています。
Tristan Reid、

26

sort変更される配列を気にしない場合は、を使用してください。

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

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

3
フルソートは最小/最大を見つけるための最速の方法ではありませんが、私はそれがうまくいくと思います。
J.ホームズ

4
これはを変更することに注意してください。これはmyArray予期しないことです。
ジーゼマー

配列のソートは、それを走査するよりも低速です。並べ替えの複雑さ:O(nlog(n))、配列をトラバース:O(n)
Mourad Qqch

17

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));

14
Spread Operatorを使用するES6では、は不要になりましたapply。単に言う- Math.min(...myArray.map(o => o.Cost))最小値 Math.max(...myArray.map(o => o.Cost))を見つけるためと最大値を見つけるため。
Nitin 2016

13

ロブ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; }));

これらは、より便利で特定の形式に簡単にカレー化できます。


4
私たちのソリューションをベンチマークしました:jsperf.com/comparison-of-numbers。コードを最適化した後(ベンチマークを参照)、両方の方法のパフォーマンスは類似しています。最適化しないと、私の方法は14倍速くなります。
Rob W

2
@RoBWああ、私はあなたのバージョンがずっと速くなることを完全に期待します、私は代替のアーキテクチャの実装を提供していました。:)
J.ホームズ

@ 32bitkid同じことを期待していましたが、驚くべきことに、ベンチマークのテストケース3で見られるように、メソッドは(最適化後)ほぼ同じくらい高速です。
Rob W

1
@RobW私は同意する、私はそれを期待していなかっただろう。興味をそそられる。:)仮定するのではなく、常にベンチマークする必要があることを示しに行きます。
J.ホームズ

@RobWただし、明確にするために、ブラウザーの結果が増えると、実装は最適化されていないバージョンと最適化されたバージョンのどちらかを常に上回ります。
J.ホームズ

6

マックスのために

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

分の

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

5

試してください(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);


2
コンパクトで使いやすいこの回答が大好きです。
ディーン

3

Array.prototype.reduce()を使用すると、コンパレータ関数をプラグインして、配列内の最小、最大などの項目を決定できます。

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;


3

使用する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);


2

これはより良い解決策です

    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);

2

Tristan Reidの答え(+ es6を使用)に追加して、コールバックを受け入れる関数を作成できます。これには、prevandに適用する演算子が含まれますcurr

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);

2

これは、lodashのminByand maxBy関数で実現できます。

Lodash minBymaxByドキュメント

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

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

これらのメソッドは、配列内の各要素に対して呼び出されるiterateeを受け入れ、値がランク付けされる基準を生成します。iterateeは1つの引数で呼び出されます:(値)。

解決

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>


0

2つのアプローチで問題を解決することができます。両方の方法はすでに上記で説明されていますが、パフォーマンステストが欠落していたため、その1つを完了します

1、ネイティブのJavaスクリプトの方法
2、最初にオブジェクトをソートしてから、ソートされたobjからmin 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)


0

簡潔で最新のソリューションの場合reduce、現在の最小値と最大値を追跡しながら、配列に対して操作を実行できるため、配列は1回だけ反復されます(これが最適です)。

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

デモ:


-2

もう1つは、ケネベックの回答に似ていますが、すべて1行で表示されます。

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

-2

代わりに組み込みの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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.