Minimum / Maks Elde Etmek için JavaScript Nesne Dizisini Karşılaştırın


102

Bir dizi nesnem var ve bu nesneleri belirli bir nesne özelliği üzerinde karşılaştırmak istiyorum. İşte dizim:

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

Özellikle "maliyet" üzerine sıfırlamak ve bir minimum ve maksimum değer elde etmek istiyorum. Maliyet değerlerini alıp bir javascript dizisine aktarabileceğimi ve ardından Hızlı JavaScript Maks / Min'i çalıştırabileceğimi fark ettim .

Bununla birlikte, ortadaki dizi adımını atlayarak ve doğrudan nesnelerin özelliklerinden (bu durumda "Maliyet") çıkarak bunu yapmanın daha kolay bir yolu var mı?

Yanıtlar:


56

Bunun bir yolu, tüm öğeler arasında döngü yapmak ve bunu en yüksek / en düşük değerle karşılaştırmaktır.

(Bir dizi oluşturmak, dizi yöntemlerini çağırmak bu basit işlem için aşırıdır).

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

Bu mantıklı, harici bir yüksek / düşük sayı yerine dizinin içindeki verileri birbiriyle karşılaştırmayı düşünmek zorunda kaldım.
firedrawndagger

1
Değiştireceğim tek şey, en düşük ve en yüksek ayarı biraz fazlalıktır. Bir kere daha az döngü yapmayı lowest=highest=myArray[0]ve döngüyü 1'de başlatmayı tercih ederim .
J. Holmes

1
@ 32bitkid İyi nokta. myArray[0].Costolsa da olmalı . Ancak, ilk öğe yoksa, bir hata atılacaktır. Bu nedenle, muhtemelen küçük performans artışını ortadan kaldıran ek bir kontrol gereklidir.
Rob W

1
@Wilt Evet, en düşük değeri bulduğunuzda güncellenen başka bir değişkeni koruyun, yani var lowestObject; for (...)veif (tmp < lowest) { lowestObject = myArray[i]; lowest = tmp; }
Rob W

1
ECMAScript 2015 (ES6) çıkmadan önce bu cevap çok eskidir. O zaman haklıydı, ama şimdi bu cevap daha iyi bir seçenek.
Erick Petrucelli

176

Azaltma, şunun gibi şeyler için iyidir: bir dizi nesne üzerinde toplu işlemler (min, maks, ortalama, vb.) Gerçekleştirmek ve tek bir sonuç döndürmek için:

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

... veya bu iç işlevi ES6 işlev sözdizimi ile tanımlayabilirsiniz:

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

Sevimli olmak istiyorsanız, bunu diziye ekleyebilirsiniz:

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

Şimdi şunu söyleyebilirsin:

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

Lütfen bunu kullanmayı planlıyorsanız, her durum için tam kontrollerin olmadığını unutmayın. Bir dizi ilkel türden geçerseniz, başarısız olur. Var olmayan bir özelliği kontrol ederseniz veya tüm nesneler bu özelliği içermiyorsa, son öğeyi alırsınız. Bu sürüm biraz daha hantal, ancak şu kontrollere sahip:

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

17
Bence en iyi cevap. Diziyi değiştirmez ve "Bir dizi oluşturmak, dizi yöntemlerini çağırmak bu basit işlem için aşırıdır" diyen yanıttan çok daha özlü
Julian Mann

Bu, büyük veri kümelerinin (30+ sütun / 100.000 satır) performansı için mutlak en iyi cevaptır.
cerd

2
Merak ediyorum, azaltma kontrolleri ne zaman dizinin ilk elemanı prev.Costtanımsız olmayacak ? Yoksa 0 mı başlıyor?
GrayedFox

1
İyi nokta @Saheb. Sadece düzenledim, bu durumda bu durumda boş dönecek.
Tristan Reid

1
Ayrıca, nesne olmayanlar gibi sorunlara neden olabilecek diğer girdiler için veya bu özelliğin bazı nesnelerde eksik olup olmadığı için bazı kontroller ekledim. Nihayetinde, sanırım çoğu vaka için biraz ağırlaşıyor.
Tristan Reid

28

sortDizinin değiştirilmesiyle ilgilenmiyorsanız kullanın .

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

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

3
tam sıralama, min / maks. bulmanın en hızlı yolu değildir, ancak işe yarayacağını tahmin ediyorum.
J. Holmes

4
Bunun myArraybeklenmeyebilecek şekilde değişeceğini unutmayın.
ziesemer

Bir diziyi sıralamak, onu dolaşmaktan daha yavaştır. Karmaşıklığı sırala:, O(nlog(n))bir dizi üzerinden geçerek:O(n)
Mourad Qqch

20

MathFonksiyonları kullanın ve istediğiniz değerleri çıkarın map.

İşte 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

GÜNCELLEME:

ES6 kullanmak istiyorsanız:

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

19
Yayılma Operatörü kullanan ES6'da artık ihtiyacımız yok apply. Basitçe şunu söyleyin - Math.min(...myArray.map(o => o.Cost))minimumu Math.max(...myArray.map(o => o.Cost))bulmak ve maksimumu bulmak için.
Nitin

13

Bence Rob W'nin cevabı gerçekten doğru cevap (+1), ama sadece eğlenmek için: "zeki" olmak istiyorsan, şöyle bir şey yapabilirsin:

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

veya derinlemesine iç içe geçmiş bir yapınız varsa, biraz daha işlevsel hale gelebilir ve aşağıdakileri yapabilirsiniz:

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

Bunlar kolaylıkla daha kullanışlı / özel biçimlere dönüştürülebilir.


4
Çözümlerimizi karşılaştırdım: jsperf.com/comparison-of-numbers . Kodunuzu optimize ettikten sonra (karşılaştırmaya bakın), her iki yöntemin performansı benzerdir. Optimizasyon olmadan yöntemim 14 kat daha hızlıdır.
Rob W

2
@RoBW ah ben tamamen sizin versiyonu olmasını beklediğiniz yolu daha hızlı, sadece alternatif bir mimari uygulama sağlıyordu. :)
J. Holmes

@ 32bitkid Ben de aynısını bekliyordum, ancak şaşırtıcı bir şekilde yöntem, kıyaslama testinin 3. test örneğinde görüldüğü gibi (optimizasyondan sonra) neredeyse hızlı.
Rob W

1
@RobW Katılıyorum, bunu beklemiyordum. Merak ediyorum. :) Her zaman varsaymak yerine kıyaslama yapmanız gerektiğini göstermeye gider.
J. Holmes

@RobW Yine de açık olmak gerekirse, daha fazla tarayıcı sonucuyla uygulamanızın ya optimize edilmemiş ve optimize edilmiş sürümleri sürekli olarak yeneceğini düşünüyorum.
J. Holmes

7

Try ( adizidir, fkarşılaştırılacak alandır)

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
Bu yanıtı seviyorum, çok derli toplu ve kullanımı kolay.
Dean

6

Max için

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

dakika için

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

4

Array.prototype.reduce () kullanarak , bir dizideki min, max, vb. Öğeleri belirlemek için karşılaştırma işlevlerini ekleyebilirsiniz.

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;


4

Kullanılması Math.minve 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);


2

Bu daha iyi bir çözüm

    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'in cevabına (+ es6 kullanarak) ekleyerek, geri aramayı kabul eden bir işlev yaratabilirsin, bu fonksiyonun uygulanmasını istediğiniz operatörü prevve 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

O zaman bunu kullanarak arayabilirsiniz:

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

2

Bu, Lodash minByvemaxBy işlevlerle .

Lodash minByve maxBybelgeler

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

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

Bu yöntemler, değerin sıralandığı kriteri oluşturmak için dizideki her öğe için çağrılan bir yinelemeyi kabul eder. Yineleme, bir bağımsız değişkenle çağrılır: (değer).

Çözüm

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

problemi iki yaklaşımla çözebiliriz, her iki yöntem de yukarıda açıklanmıştır ancak performans testi eksikti, bu yüzden onu tamamlamak

1, yerel java-script yolu
2, önce nesneyi sıralayın, ardından sıralanmış nesneden minimum maksimum elde etmek kolaydır

Ayrıca her iki çekme yaklaşımının performansını da test ediyorum

ayrıca performansı çalıştırabilir ve test edebilirsiniz ... Mutlu kodlamalar (:

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

Kısa ve modern bir çözüm için, reducemevcut minimum ve maksimum değerleri takip ederek dizi üzerinde bir işlem gerçekleştirilebilir , böylece dizi yalnızca bir kez yinelenir (en uygun olanıdır).

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

Demo:


-2

Başka bir soru, Kennebec'in cevabına benzer, ancak hepsi tek satırda:

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

-2

Bunun yerine Math.max / Math.min'i kullanmak için yerleşik Array nesnesini kullanabilirsiniz:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.