Wie kann ich leicht erhalten die minimale oder maximale Element eines JavaScript-Array?
Beispiel Pseudocode:
let array = [100, 0, 50]
array.min() //=> 0
array.max() //=> 100
Wie kann ich leicht erhalten die minimale oder maximale Element eines JavaScript-Array?
Beispiel Pseudocode:
let array = [100, 0, 50]
array.min() //=> 0
array.max() //=> 100
Wie wäre es, das eingebaute Array-Objekt zu erweitern, um Math.max
/ Math.min
stattdessen:
Array.prototype.max = function() {
return Math.max.apply(null, this);
};
Array.prototype.min = function() {
return Math.min.apply(null, this);
};
Hier ist ein JSFiddle .
Das Erweitern der Built-Ins kann zu Kollisionen mit anderen Bibliotheken führen (einige sehen), daher ist es vielleicht besser, wenn Sie nur apply
ing Math.xxx()
direkt mit Ihrem Array verbinden:
var min = Math.min.apply(null, arr),
max = Math.max.apply(null, arr);
Alternativ können Sie, sofern Ihr Browser ECMAScript 6 unterstützt, die Spread-Operator der ähnlich funktioniert wie der apply
Methode:
var min = Math.min( ...arr ),
max = Math.max( ...arr );
Müsste es nicht heißen "return Math.max.apply( Math, this );" und nicht return Math.max.apply( null, this );
@HankH: vielleicht. Math.max
ist wie eine "statische" Methode, so dass es keine nützliche this
Instanz darin (hoffe ich). Angenommen, das ist wahr, dann würde der Aufruf im globalen Bereich erfolgen (d.h. window
), was gleichbedeutend ist mit der Übergabe von null
als ersten Parameter für apply
/ call
.
@HankH: vorbei null
o Math
o {}
oder was auch immer an apply()
o call()
hat keinen Einfluss auf das Ergebnis. Math.max
verweist nicht und sollte nicht verweisen this
innerlich.
var max_of_array = Math.max.apply(Math, array);
Für eine ausführliche Diskussion siehe: http://aaroncrane.co.uk/2008/11/javascript_max_api/
Was ist der Unterschied zwischen Math.max.apply(Math, array)
y Math.max.apply(null, array)
? Im Blog heißt es: "...man muss auch überflüssigerweise wieder sagen, dass max
gehört zu Math
...", aber es scheint, dass ich das nicht tun muss (indem ich das erste Argument von apply
als null
).
@DavidPortabella Ich weiß nicht, warum das lustig ist. So funktioniert es nach der Spezifikation : If no arguments are given, the result is -.
Für große Arrays (~10 Elemente), Math.min
y Math.max
erzeugt in Node.js den folgenden Fehler.
BereichFehler: Maximale Größe des Aufrufstapels überschritten
Eine robustere Lösung besteht darin, nicht jedes Element zum Aufrufstapel hinzuzufügen, sondern stattdessen ein Array zu übergeben:
function arrayMin(arr) {
return arr.reduce(function (p, v) {
return ( p < v ? p : v );
});
}
function arrayMax(arr) {
return arr.reduce(function (p, v) {
return ( p > v ? p : v );
});
}
Wenn Sie sich Sorgen um die Geschwindigkeit machen, ist der folgende Code ~3 mal schneller als Math.max.apply
befindet sich auf meinem Computer. Siehe https://jsben.ch/JPOyL .
function arrayMin(arr) {
var len = arr.length, min = Infinity;
while (len--) {
if (arr[len] < min) {
min = arr[len];
}
}
return min;
};
function arrayMax(arr) {
var len = arr.length, max = -Infinity;
while (len--) {
if (arr[len] > max) {
max = arr[len];
}
}
return max;
};
Wenn Ihre Arrays Strings anstelle von Zahlen enthalten, müssen Sie diese ebenfalls in Zahlen umwandeln. Der folgende Code tut das, aber es verlangsamt den Code ~10 mal auf meinem Rechner. Siehe https://jsben.ch/uPipD .
function arrayMin(arr) {
var len = arr.length, min = Infinity;
while (len--) {
if (Number(arr[len]) < min) {
min = Number(arr[len]);
}
}
return min;
};
function arrayMax(arr) {
var len = arr.length, max = -Infinity;
while (len--) {
if (Number(arr[len]) > max) {
max = Number(arr[len]);
}
}
return max;
};
Zuweisen. min
y max
bis zum letzten Element und reduzieren Sie die Iterationen um 1 ( while(--len)
) ;)
@Venugopal dann brauchen Sie eine spezielle Prüfung, um festzustellen, ob das Array leer ist, und geben Sie +/- Infinity zurück
Seltsam... Ich ging auf die verlinkte Website ... und Tests in Firefox 51.0.0 / Mac OS X 10.12.0, die reduzieren-basierte Ansatz ist 30% langsamer als Schleife-basierte ... sehr unterschiedliche Ergebnisse
// For regular arrays:
var max = Math.max(...arrayOfNumbers);
// For arrays with tens of thousands of items:
let max = testArray[0];
for (let i = 1; i < testArrayLength; ++i) {
if (testArray[i] > max) {
max = testArray[i];
}
}
Le site offizielle MDN-Dokumente über Math.max()
deckt dieses Thema bereits ab:
Die folgende Funktion verwendet Function.prototype.apply() um das maximale Element in einem numerischen Feld zu finden.
getMaxOfArray([1, 2, 3])
ist gleichbedeutend mitMath.max(1, 2, 3)
, aber Sie könnengetMaxOfArray()
auf programmatisch konstruierte Arrays beliebiger Größe.function getMaxOfArray(numArray) { return Math.max.apply(null, numArray); }
Oder mit dem neuen Spread-Operator wird es viel einfacher, das Maximum eines Arrays zu ermitteln.
var arr = [1, 2, 3]; var max = Math.max(...arr);
Laut MDN die apply
und verbreitete Lösungen hatte eine Begrenzung auf 65536, die sich aus der Begrenzung der maximalen Anzahl von Argumenten ergibt:
Aber Vorsicht: Wenn Sie apply auf diese Weise verwenden, besteht die Gefahr, dass Sie das Limit für die Argumentlänge der JavaScript-Engine überschreiten. Die Folgen der Anwendung einer Funktion mit zu vielen Argumenten (denken Sie an mehr als Zehntausende von Argumenten) variieren je nach Engine ( JavaScriptCore hat ein fest kodiertes Argumentlimit von 65536 ), da der Grenzwert (und sogar die Art eines übermäßig großen Stapels) nicht spezifiziert ist. Einige Maschinen werden eine Ausnahme auslösen. Andere wiederum begrenzen willkürlich die Anzahl der Argumente, die tatsächlich an die angewandte Funktion übergeben werden. Zur Veranschaulichung des letzteren Falles: Wenn eine solche Engine ein Limit von vier Argumenten hätte (die tatsächlichen Limits sind natürlich wesentlich höher), wäre es so, als ob in den obigen Beispielen die Argumente 5, 6, 2, 3 an apply übergeben worden wären und nicht das gesamte Array.
Sie bieten sogar eine Hybridlösung an, die im Vergleich zu anderen Lösungen nicht wirklich leistungsfähig ist. Siehe Leistungstest unten für mehr.
Im Jahr 2019 wird die das tatsächliche Limit ist die maximale Größe des Aufrufstapels . Für moderne Chromium-basierte Desktop-Browser bedeutet dies, dass bei der Suche nach min/max mit apply
oder verbreitet, Praktisch ist die maximale Größe für reine Zahlen-Arrays ~120000 . Darüber hinaus kommt es zu einem Stapelüberlauf und die folgende Fehlermeldung wird ausgegeben:
BereichFehler: Maximale Größe des Aufrufstapels überschritten
Mit dem folgenden Skript (basierend auf dieser Blogbeitrag ), können Sie durch Abfangen dieses Fehlers den Grenzwert für Ihre spezifische Umgebung berechnen.
Warnung! Die Ausführung dieses Skripts nimmt Zeit in Anspruch und kann je nach der Leistung Ihres Systems Ihren Browser/System verlangsamen oder zum Absturz bringen!
let testArray = Array.from({length: 10000}, () => Math.floor(Math.random() * 2000000));
for (i = 10000; i < 1000000; ++i) {
testArray.push(Math.floor(Math.random() * 2000000));
try {
Math.max.apply(null, testArray);
} catch (e) {
console.log(i);
break;
}
}
Auf der Grundlage des Tests in EscapeNetscape Kommentar habe ich einige Benchmarks erstellt, die 5 verschiedene Methoden auf einem Zufallszahl nur Array mit 100000 Elementen .
Für 2019 zeigen die Ergebnisse, dass die Standardschleife (das übrigens keine Größenbeschränkung hat) ist überall am schnellsten. apply
und verbreiten folgt dicht darauf, dann viel später die Hybridlösung von MDN, dann reduce
als die langsamste.
Fast alle Tests ergaben die gleichen Ergebnisse, mit Ausnahme eines Tests, bei dem die Ausbreitung irgendwie am langsamsten war.
Wenn Sie Ihr Array auf 1 Million Elemente erweitern, wird es unübersichtlich, und es bleibt nur noch die Standardschleife als schnelle Lösung und reduce
als ein langsamerer.
var testArrayLength = 100000
var testArray = Array.from({length: testArrayLength}, () => Math.floor(Math.random() * 2000000));
// ES6 spread
Math.min(...testArray);
Math.max(...testArray);
// reduce
testArray.reduce(function(a, b) {
return Math.max(a, b);
});
testArray.reduce(function(a, b) {
return Math.min(a, b);
});
// apply
Math.min.apply(Math, testArray);
Math.max.apply(Math, testArray);
// standard loop
let max = testArray[0];
for (let i = 1; i < testArrayLength; ++i) {
if (testArray[i] > max) {
max = testArray[i];
}
}
let min = testArray[0];
for (let i = 1; i < testArrayLength; ++i) {
if (testArray[i] < min) {
min = testArray[i];
}
}
// MDN hibrid soltuion
// Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Using_apply_and_built-in_functions
function minOfArray(arr) {
var min = Infinity;
var QUANTUM = 32768;
for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
min = Math.min(submin, min);
}
return min;
}
minOfArray(testArray);
function maxOfArray(arr) {
var max = -Infinity;
var QUANTUM = 32768;
for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
var submax = Math.max.apply(null, arr.slice(i, Math.max(i + QUANTUM, len)));
max = Math.max(submax, max);
}
return max;
}
maxOfArray(testArray);
Wenn Sie Typescript verwenden, wird der gezeigte Spread-Operator kompiliert zu Math.max.apply(Math, arr)
für 'max'-Kompatibilität.
Ebenfalls von MDN: "beide verbreiten (...)
y apply
wird entweder fehlschlagen oder das falsche Ergebnis zurückgeben, wenn das Array zu viele Elemente hat [...] Die reduzierte Lösung hat dieses Problem nicht." Beim Testen von Chrome, FF, Edge und IE11 scheint es, dass es für ein Array mit bis zu 100k Werten in Ordnung ist. (Getestet auf Win10 und den neuesten Browsern: Chrome 110k, Firefox 300k, Edge 400k, IE11 150k).
Dies ist eine sehr langsame Methode, was wäre, wenn das Feld Tausende von Elementen hätte?
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
178 Stimmen
Anmerkung: Mit ECMAScript 6 können Sie die neue Spread-Operator (drei Punkte:
...
) mitMath.max()
wie diese:Math.max(...[2, 5, 16, 1])
. Siehe meine Antwort aus dem MDN-Dokumentation .0 Stimmen
Hier ein Benchmark für einen Geschwindigkeitsvergleich der gebräuchlichsten Vorgehensweisen: jsben.ch/#/1QuTg
0 Stimmen
Ohne ES6
Math.max.apply(null, [2,5,16,1])
2 Stimmen
In ES6 können sowohl das Maximum als auch das Minimum wie folgt ermittelt werden mit nur einem
reduce
aufrufen .0 Stimmen
Die Lösung von @totymedli, den Spread-Operator zu verwenden, ist keine gute Idee. Wenn das Array zu groß ist, führt dies zu einer Stack Overflow Exception (zu viele Parameter werden an die Funktion übergeben). Eine viel bessere Idee ist die Verwendung von
reduce
1 Stimmen
@AronFiechter Haben Sie meine Antwort tatsächlich gelesen? Ich erkläre alle Optionen sehr detailliert mit Codebeispielen und Benchmarks. Die Größe des Aufrufstapels ist nur dann ein Problem, wenn Ihre Arrays eine Größe von mehr als 100000 haben. Der Aufrufstapel muss zwar berücksichtigt werden, aber in den meisten Fällen ist er kein Problem und der prägnantere Code überwiegt die Nachteile.
1 Stimmen
Dieser Aufrufstapel kann ein Problem darstellen. Es gibt eine HackerRank-Frage, bei der Min und Max gefunden werden müssen, und die Tests laufen unter einem Limit von 10 Sekunden. Die von HackerRank für den 9. bis 14. Test übergebenen Arrays haben eine Länge von mehr als 100.000 und schlagen fehl, wenn die reduzierte Lösung in der Antwort unten verwendet wird. Die for-Schleife wird für einige
0 Stimmen
Utilice
...
(Spread-Operator):const maxValue = Math.max(...array))