Bandingkan JavaScript Array of Objects untuk Mendapatkan Min / Max

95

Saya memiliki array objek dan saya ingin membandingkan objek tersebut pada properti objek tertentu. Inilah array saya:

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

Saya ingin membidik secara spesifik "biaya" dan mendapatkan nilai minimum dan maksimum. Saya menyadari bahwa saya bisa mengambil nilai biaya dan mendorongnya ke dalam array javascript dan kemudian menjalankan Fast JavaScript Max / Min .

Namun, adakah cara yang lebih mudah untuk melakukan ini dengan melewati langkah array di tengah dan menonaktifkan properti objek (dalam hal ini "Biaya") secara langsung?

dipecat
sumber

Jawaban:

53

Cara tercepat, dalam hal ini, adalah mengulang semua elemen, dan membandingkannya dengan nilai tertinggi / terendah, sejauh ini.

(Membuat sebuah larik, memanggil metode larik adalah berlebihan untuk operasi sederhana ini).

 // 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.
sumber
Ini masuk akal, saya terjebak dengan pemikiran tentang membandingkan data di dalam array satu sama lain alih-alih angka tinggi / rendah eksternal.
dipecatrawndagger
1
Satu-satunya hal yang akan saya ubah adalah menetapkan terendah dan tertinggi agak berlebihan. Saya lebih suka mengulang satu kali lebih sedikit dan mengatur lowest=highest=myArray[0]dan kemudian memulai loop pada 1.
J. Holmes
1
@ 32. 32 Poin yang bagus. seharusnya begitu myArray[0].Cost. Tapi, jika tidak ada elemen pertama, kesalahan akan muncul. Jadi, pemeriksaan tambahan diperlukan, mungkin membatalkan peningkatan kinerja kecil.
Rob W
Saya datang ke sini karena saya ingin benda itu sendiri dikembalikan. Bukan nilai terendah, itu mudah. Ada saran?
Wilt
1
@Wilt Ya, pertahankan variabel lain yang diperbarui saat Anda menemukan nilai terendah, yaitu var lowestObject; for (...)danif (tmp < lowest) { lowestObject = myArray[i]; lowest = tmp; }
Rob W
163

Mengurangi bagus untuk hal-hal seperti ini: untuk melakukan operasi agregat (seperti min, max, avg, dll.) Pada larik objek, dan mengembalikan satu hasil:

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

... atau Anda dapat menentukan fungsi dalam itu dengan sintaks fungsi ES6:

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

Jika Anda ingin menjadi lucu, Anda dapat melampirkan ini ke array:

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

Sekarang Anda bisa mengatakan:

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

Harap dicatat bahwa jika Anda berniat untuk menggunakan ini, tidak ada pemeriksaan penuh untuk setiap situasi. Jika Anda meneruskan larik tipe primitif, itu akan gagal. Jika Anda memeriksa properti yang tidak ada, atau jika tidak semua objek berisi properti itu, Anda akan mendapatkan elemen terakhir. Versi ini sedikit lebih besar, tetapi memiliki pemeriksaan tersebut:

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;
 }
Tristan Reid
sumber
14
Jawaban terbaik menurut saya. Itu tidak mengubah array dan ini jauh lebih ringkas daripada jawaban yang mengatakan "Membuat array, memanggil metode array berlebihan untuk operasi sederhana ini"
Julian Mann
Ini adalah jawaban terbaik mutlak untuk kinerja kumpulan data besar (30+ kolom / 100 ribu baris).
cerd
2
Hanya ingin tahu, ketika dikurangi pemeriksaan, elemen pertama dari array tidak prev.Costakan ditentukan? Atau apakah itu dimulai sebagai 0?
GrayedFox
1
Poin bagus @ Saheb. Saya baru saja mengedit sehingga akan mengembalikan nol dalam kasus itu.
Tristan Reid
1
Saya juga menambahkan beberapa pemeriksaan untuk input lain yang dapat menyebabkan masalah, seperti non-objek, atau jika properti itu hilang dari beberapa objek. Pada akhirnya itu menjadi sedikit berat untuk sebagian besar kasus saya pikir.
Tristan Reid
26

Gunakan sort, jika Anda tidak peduli tentang larik yang sedang dimodifikasi.

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

var min = myArray[0],
    max = myArray[myArray.length - 1]
katspaugh
sumber
3
jenis lengkap bukanlah cara tercepat untuk menemukan min / max tetapi saya rasa itu akan berhasil.
J.Holmes
4
Ketahuilah bahwa ini akan mengubah myArray, yang mungkin tidak diharapkan.
ziesemer
Mengurutkan sebuah array lebih lambat daripada mengurutkannya. Urutkan kompleksitas O(nlog(n))O(n)
:,
18

Gunakan Mathfungsi dan ambil nilai yang Anda inginkan map.

Ini jsbinnya:

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

MEMPERBARUI:

Jika Anda ingin menggunakan ES6:

var min = Math.min.apply(null, myArray.map(item => item.Cost)),
    max = Math.max.apply(null, myArray.map(item => item.Cost));
Rupert
sumber
15
Di ES6 menggunakan Spread Operator, kita tidak perlu lagi apply. Katakan saja - Math.min(...myArray.map(o => o.Cost))untuk menemukan minimum dan Math.max(...myArray.map(o => o.Cost))untuk menemukan maksimum.
Nitin
13

Saya pikir jawaban Rob W benar-benar benar (+1), tetapi hanya untuk kesenangan: jika Anda ingin menjadi "pintar", Anda dapat melakukan sesuatu seperti ini:

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

atau jika Anda memiliki struktur yang sangat bersarang, Anda bisa sedikit lebih fungsional dan melakukan hal berikut:

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

Ini bisa dengan mudah dikawinkan menjadi bentuk yang lebih berguna / spesifik.

J. Holmes
sumber
4
Saya telah membandingkan solusi kami: jsperf.com/comparison-of-numbers . Setelah mengoptimalkan kode Anda (lihat tolok ukur), performa kedua metode tersebut serupa. Tanpa pengoptimalan, metode saya 14x lebih cepat.
Rob W
2
@RoBW oh saya benar-benar harapkan versi Anda menjadi cara cepat, saya hanya memberikan sebuah implementasi arsitektur alternatif. :)
J.Holmes
@ 32bitkid Saya mengharapkan hal yang sama, tetapi yang mengejutkan, metode ini hampir secepat (setelah pengoptimalan), seperti yang terlihat pada kasus uji 3 dari tolok ukur.
Rob W
1
@ RobW saya setuju, saya tidak akan mengharapkan itu. Saya tertarik. :) Menunjukkan bahwa Anda harus selalu menjadi patokan daripada berasumsi.
J.Holmes
@RobW Hanya untuk memperjelas, saya pikir dengan lebih banyak hasil browser, implementasi Anda akan secara konsisten mengalahkan versi yang tidak dioptimalkan dan dioptimalkan.
J.Holmes
6

untuk Max

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

untuk min

Math.min.apply(Math, myArray.map(a => a.Cost));
Issa Lafi
sumber
5

Coba ( aadalah larik, fadalah bidang untuk dibandingkan)

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

Kamil Kiełczewski
sumber
2
Suka jawaban ini, begitu ringkas dan mudah digunakan.
Dean
3

Menggunakan Array.prototype.reduce () , Anda dapat menyambungkan fungsi komparator untuk menentukan item min, max, dll. Dalam sebuah array.

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;

Tuan Polywhirl
sumber
3

Menggunakan Math.mindan Math.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);

JuZDePeche
sumber
2

Ini solusi yang lebih baik

    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);
Deepak Sisodiya
sumber
2

Menambahkan ke jawaban Tristan Reid (+ menggunakan es6), Anda dapat membuat fungsi yang menerima panggilan balik, yang akan berisi operator yang ingin Anda terapkan prevdan 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

Kemudian Anda cukup menyebutnya menggunakan:

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

Ini bisa dicapai dengan lodash minBydan maxByfungsinya.

Lodash minBydan maxBydokumentasi

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

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

Metode ini menerima iteratee yang dipanggil untuk setiap elemen dalam array untuk menghasilkan kriteria yang digunakan untuk menentukan peringkat nilai. Iteratee dipanggil dengan satu argumen: (nilai).

Larutan

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>

Trent
sumber
0

kita bisa menyelesaikan masalah dengan dua pendekatan kedua metode sudah dijelaskan di atas tetapi uji kinerja tidak ada sehingga menyelesaikan yang satu itu

1, cara asli java-script
2, pertama urutkan objek kemudian mudah untuk mendapatkan min max dari objek yang diurutkan

saya juga menguji kinerja kedua pendekatan derek

Anda juga dapat menjalankan dan menguji kinerja ... Selamat coding (:

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

Jadli
sumber
0

Untuk solusi yang ringkas dan modern, seseorang dapat melakukan reduceoperasi di atas larik, dengan melacak nilai minimum dan maksimum saat ini, sehingga larik hanya diulangi sekali (yang optimal).

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

Demo:

sedikit
sumber
-2

Jawaban lainnya, mirip dengan jawaban Kennebec, tetapi semuanya dalam satu baris:

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

Anda dapat menggunakan objek Array bawaan untuk menggunakan Math.max / Math.min sebagai gantinya:

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
Kode sumber
sumber