Vergleichen Sie das JavaScript-Array von Objekten, um Min / Max zu erhalten

94

Ich habe ein Array von Objekten und möchte diese Objekte auf einer bestimmten Objekteigenschaft vergleichen. Hier ist mein Array:

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

Ich möchte mich speziell auf die "Kosten" konzentrieren und einen minimalen und maximalen Wert erhalten. Mir ist klar, dass ich einfach die Kostenwerte abrufen und in ein Javascript-Array verschieben und dann das schnelle JavaScript Max / Min ausführen kann .

Gibt es jedoch eine einfachere Möglichkeit, dies zu tun, indem Sie den Array-Schritt in der Mitte umgehen und die Objekteigenschaften (in diesem Fall "Kosten") direkt verlassen?

Feuerdolch
quelle

Antworten:

53

In diesem Fall besteht der schnellste Weg darin, alle Elemente zu durchlaufen und mit dem bisher höchsten / niedrigsten Wert zu vergleichen.

(Das Erstellen eines Arrays und das Aufrufen von Array-Methoden ist für diese einfache Operation übertrieben.)

 // 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.
quelle
Dies ist sinnvoll. Ich habe darüber nachgedacht, Daten innerhalb des Arrays miteinander zu vergleichen, anstatt eine externe hohe / niedrige Zahl.
feuerrawndagger
1
Das einzige, was ich ändern würde, ist die niedrigste und höchste Einstellung ist etwas redundant. Ich würde lieber ein Mal weniger schleifen lowest=highest=myArray[0]und die Schleife setzen und dann um 1 beginnen.
J. Holmes
1
@ 32bitkid Guter Punkt. sollte es myArray[0].Costaber sein. Wenn jedoch kein erstes Element vorhanden ist, wird ein Fehler ausgegeben. Daher ist eine zusätzliche Überprüfung erforderlich, die möglicherweise den kleinen Leistungsschub rückgängig macht.
Rob W
Ich bin hierher gekommen, weil ich möchte, dass das Objekt selbst zurückgegeben wird. Nicht der niedrigste Wert, was einfach war. Irgendwelche Vorschläge?
Wilt
1
@Wilt Ja, pflegen Sie eine andere Variable, die aktualisiert wird, wenn Sie einen niedrigsten Wert gefunden haben, dh var lowestObject; for (...)undif (tmp < lowest) { lowestObject = myArray[i]; lowest = tmp; }
Rob W
158

Die Reduzierung ist gut für solche Dinge: um aggregierte Operationen (wie min, max, avg usw.) für ein Array von Objekten auszuführen und ein einzelnes Ergebnis zurückzugeben:

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

... oder Sie können diese innere Funktion mit der ES6-Funktionssyntax definieren:

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

Wenn du süß sein willst, kannst du dies an ein Array anhängen:

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

Jetzt können Sie einfach sagen:

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

Bitte beachten Sie, dass wenn Sie dies verwenden möchten, nicht für jede Situation eine vollständige Überprüfung durchgeführt wird. Wenn Sie ein Array primitiver Typen übergeben, schlägt dies fehl. Wenn Sie nach einer Eigenschaft suchen, die nicht vorhanden ist, oder wenn nicht alle Objekte diese Eigenschaft enthalten, erhalten Sie das letzte Element. Diese Version ist etwas sperriger, hat aber folgende Überprüfungen:

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
quelle
13
Beste Antwort meiner Meinung nach. Es ändert das Array nicht und ist weitaus prägnanter als die Antwort "Das Erstellen eines Arrays und das Aufrufen von Array-Methoden ist für diese einfache Operation übertrieben"
Julian Mann,
Dies ist die absolut beste Antwort für die Leistung großer Datenmengen (über 30 Spalten / 100.000 Zeilen).
Cerd
2
Sie fragen sich nur, wann bei reduzierten Überprüfungen das erste Element des Arrays nicht prev.Costundefiniert ist? Oder wird es als 0 initiiert?
GrayedFox
1
Guter Punkt @Saheb. Ich habe gerade bearbeitet, so dass in diesem Fall null zurückgegeben wird.
Tristan Reid
1
Ich habe auch einige Überprüfungen für andere Eingaben hinzugefügt, die Probleme verursachen könnten, z. B. Nicht-Objekte, oder ob diese Eigenschaft in einigen Objekten fehlte. Letztendlich wird es in den meisten Fällen, denke ich, ein wenig hartnäckig.
Tristan Reid
25

Verwenden sortSie diese Option , wenn Sie sich nicht für das zu ändernde Array interessieren.

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

var min = myArray[0],
    max = myArray[myArray.length - 1]
katspaugh
quelle
3
Eine vollständige Sortierung ist nicht der schnellste Weg, um Min / Max zu finden, aber ich denke, es wird funktionieren.
J. Holmes
4
Beachten Sie jedoch, dass sich dies ändert myArray, was möglicherweise nicht zu erwarten ist.
ziesemer
Das Sortieren eines Arrays ist langsamer als das Durchlaufen. Komplexität O(nlog(n))O(n)
sortieren
17

Verwenden Sie MathFunktionen und ziehen Sie die gewünschten Werte heraus map.

Hier ist der 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

AKTUALISIEREN:

Wenn Sie ES6 verwenden möchten:

var min = Math.min.apply(null, myArray.map(item => item.Cost)),
    max = Math.max.apply(null, myArray.map(item => item.Cost));
Rupert
quelle
14
In ES6 mit Spread Operator brauchen wir nicht mehr apply. Sagen Sie einfach - Math.min(...myArray.map(o => o.Cost))um das Minimum und Math.max(...myArray.map(o => o.Cost))das Maximum zu finden.
Nitin
13

Ich denke, Rob Ws Antwort ist wirklich die richtige (+1), aber nur zum Spaß: Wenn Sie "klug" sein wollen, können Sie so etwas tun:

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

oder wenn Sie eine tief verschachtelte Struktur hätten, könnten Sie etwas funktionaler werden und Folgendes tun:

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

Diese könnten leicht in nützlichere / spezifischere Formen gebracht werden.

J. Holmes
quelle
4
Ich habe unsere Lösungen bewertet : jsperf.com/comparison-of-numbers . Nach der Optimierung Ihres Codes (siehe Benchmark) ist die Leistung beider Methoden ähnlich. Ohne Optimierung ist meine Methode 14x schneller.
Rob W
2
@RoBW Oh, ich würde absolut erwarten, dass Ihre Version viel schneller ist. Ich habe nur eine alternative Architekturimplementierung bereitgestellt. :)
J. Holmes
@ 32bitkid Ich habe dasselbe erwartet, aber überraschenderweise ist die Methode (nach der Optimierung) fast so schnell wie im Testfall 3 des Benchmarks.
Rob W
1
@RobW Ich stimme zu, das hätte ich nicht erwartet. Ich bin fasziniert. :) Zeigt, dass Sie immer ein Benchmarking durchführen sollten, anstatt davon auszugehen.
J. Holmes
@RobW Um ganz klar zu sein, ich denke, mit mehr Browserergebnissen würde Ihre Implementierung entweder die nicht optimierte oder die optimierte Version konsequent übertreffen.
J. Holmes
6

für max

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

für min

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

Versuchen Sie ( aist Array, fist Feld zu vergleichen)

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
quelle
2
Ich liebe diese Antwort, so kompakt und einfach zu bedienen.
Dean
3

Mit Array.prototype.reduce () können Sie Komparatorfunktionen einbinden , um das Element min, max usw. in einem Array zu bestimmen.

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;

Mr. Polywhirl
quelle
2

Dies ist eine bessere Lösung

    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
quelle
2

Wenn Sie die Antwort von Tristan Reid (+ mit es6) ergänzen, können Sie eine Funktion erstellen, die einen Rückruf akzeptiert und den Operator enthält, den Sie auf das prevund anwenden möchten 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

Dann könnten Sie es einfach nennen mit:

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

Dies kann mit lodashs minByund maxByFunktionen erreicht werden.

Lodashs minByund maxByDokumentation

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

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

Diese Methoden akzeptieren eine Iteration, die für jedes Element im Array aufgerufen wird, um das Kriterium zu generieren, nach dem der Wert eingestuft wird. Der Iterat wird mit einem Argument aufgerufen: (Wert).

Lösung

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
quelle
2

Verwenden von Math.minund 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
quelle
0

Wir können das Problem durch zwei Ansätze lösen. Beide Methoden wurden bereits oben erläutert, aber der Leistungstest fehlte, sodass dieser abgeschlossen wurde

1, native Java-Skript-Methode
2, zuerst das Objekt sortieren, dann ist es einfach, min max aus dem sortierten Objekt zu erhalten

Ich teste auch die Leistung beider Schleppansätze

Sie können auch die Leistung ausführen und testen ... Viel Spaß beim Codieren (:

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

Eine andere, ähnlich wie Kennebecs Antwort, aber alles in einer Zeile:

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

Sie können stattdessen das integrierte Array-Objekt verwenden, um Math.max / Math.min zu verwenden:

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
Quellcode
quelle