Búsqueda de sitios web

Encuentra el elemento mínimo/máximo de un arreglo en JavaScript


¿Cómo puedo obtener fácilmente el elemento mínimo o máximo de un array de JavaScript?

Ejemplo de pseudocódigo:

let array = [100, 0, 50]

array.min() //=> 0
array.max() //=> 100

Respuesta #1

¿Qué tal aumentar el objeto Array incorporado para usar Math.max/Math.min en su lugar:

Array.prototype.max = function() {
  return Math.max.apply(null, this);
};

Array.prototype.min = function() {
  return Math.min.apply(null, this);
};

let p = [35,2,65,7,8,9,12,121,33,99];

console.log(`Max value is: ${p.max()}` +
  `\nMin value is: ${p.min()}`);

Aquí está un JSFiddle.

Aumentar los elementos integrados puede causar conflictos con otras bibliotecas (algunos lo ven así), por lo que puede que te sientas más cómodo simplemente usando apply con Math.xxx() directamente en tu arreglo:

var min = Math.min.apply(null, arr),
    max = Math.max.apply(null, arr);

Alternativamente, asumiendo que su navegador soporta ECMAScript 6, puede usar la sintaxis de propagación que funciona de manera similar al método apply:

var min = Math.min( ...arr ),
    max = Math.max( ...arr );

Respuesta #2

var max_of_array = Math.max.apply(Math, array);

Para una discusión completa, vea: http://aaroncrane.co.uk/2008/11/javascript_max_api/

Respuesta #3

Usando el operador spread (ES6)

Math.max(...array)  // The same with "min" => Math.min(...array)

const array = [10, 2, 33, 4, 5];

console.log(
  Math.max(...array)
)

Respuesta #4

Para arreglos grandes (~10⁷ elementos), Math.min y Math.max ambos producen el siguiente error en Node.js.

RangeError: Tamaño máximo de pila de llamadas excedido

Una solución más sólida es no agregar cada elemento a la pila de llamadas, sino pasar un arreglo:

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

Si te preocupa la velocidad, el siguiente código es aproximadamente 3 veces más rápido que Math.max.apply en mi computadora. Véase 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;
};

Si tus arreglos contienen cadenas en lugar de números, también necesitas forzarlas a números. El código de abajo hace eso, pero desacelera el código aproximadamente 10 veces en mi máquina. Ver 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;
};

Respuesta #5

resumen

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

Solución MDN

La documentación oficial de MDN sobre Math.max() ya aborda este problema:

La siguiente función utiliza Function.prototype.apply() para encontrar el elemento máximo en un arreglo numérico. getMaxOfArray([1, 2, 3]) es equivalente a Math.max(1, 2, 3), pero puedes usar getMaxOfArray() en arreglos construidos programáticamente de cualquier tamaño.

function getMaxOfArray(numArray) {
    return Math.max.apply(null, numArray);
}

O con el nuevo operador spread, obtener el máximo de un array se vuelve mucho más fácil.

var arr = [1, 2, 3];
var max = Math.max(...arr);

Tamaño máximo de un arreglo

Según MDN, las soluciones apply y spread tenían una limitación de 65536 que provenía del límite del número máximo de argumentos:

Pero tenga cuidado: al usar apply de esta manera, corre el riesgo de exceder el límite de longitud de argumentos del motor de JavaScript. Las consecuencias de aplicar una función con demasiados argumentos (piensa en más de decenas de miles de argumentos) varían entre los motores (JavaScriptCore tiene un límite de argumentos codificado de 65536), porque el límite (de hecho, incluso la naturaleza de cualquier comportamiento de pila excesivamente grande) no está especificado. Algunos motores lanzarán una excepción. Más perniciosamente, otros limitarán arbitrariamente el número de argumentos realmente pasados a la función aplicada. Para ilustrar este último caso: si un motor así tuviera un límite de cuatro argumentos (los límites reales, por supuesto, son significativamente mayores), sería como si los argumentos 5, 6, 2, 3 hubieran sido pasados a apply en los ejemplos anteriores, en lugar del arreglo completo.

Incluso proporcionan una solución híbrida que en realidad no tiene un buen rendimiento en comparación con otras soluciones. Vea la prueba de rendimiento a continuación para más información.

En 2019, el límite real es el tamaño máximo de la pila de llamadas. Para los navegadores de escritorio modernos basados en Chromium, esto significa que cuando se trata de encontrar el mínimo/máximo con apply o spread, prácticamente el tamaño máximo para arrays de solo números es ~120000. Por encima de esto, habrá un desbordamiento de pila y se lanzará el siguiente error:

RangeError: Tamaño máximo de pila de llamadas excedido

Con el script a continuación (basado en esta publicación del blog), al capturar ese error puedes calcular el límite para tu entorno específico.

¡Advertencia! Ejecutar este script lleva tiempo y, dependiendo del rendimiento de tu sistema, podría ralentizar o hacer que tu navegador/sistema se bloquee.

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

Rendimiento en grandes matrices

Basándome en la prueba del comentario de EscapeNetscape, creé algunos benchmarks que prueban 5 métodos diferentes en un array de solo números aleatorios con 100000 elementos.

En 2019, los resultados muestran que el bucle estándar (que por cierto no tiene limitación de tamaño) es el más rápido en todas partes. apply y spread le siguen de cerca, luego mucho más tarde la solución híbrida de MDN y finalmente reduce como el más lento.

Casi todas las pruebas dieron los mismos resultados, excepto en una donde spread por alguna razón terminó siendo la más lenta.

Si aumentas tu arreglo para tener 1 millón de elementos, las cosas empiezan a romperse y te quedas con el bucle estándar como una solución rápida y reduce como una más lenta.

Benchmark de JSPerf

Benchmark de JSBen

Benchmark de JSBench.me

Código fuente de referencia

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

Respuesta #6

Si eres paranoico como yo sobre usar Math.max.apply (lo que podría causar errores cuando se le dan arrays grandes según MDN), prueba esto:

function arrayMax(array) {
  return array.reduce(function(a, b) {
    return Math.max(a, b);
  });
}

function arrayMin(array) {
  return array.reduce(function(a, b) {
    return Math.min(a, b);
  });
}

O, en ES6:

function arrayMax(array) {
  return array.reduce((a, b) => Math.max(a, b));
}

function arrayMin(array) {
  return array.reduce((a, b) => Math.min(a, b));
}

Las funciones anónimas son desafortunadamente necesarias (en lugar de usar Math.max.bind(Math) porque reduce no solo pasa a y b a su función, sino también i y una referencia al propio arreglo, por lo que debemos asegurarnos de no intentar llamar a max sobre esos también.

Respuesta #7

Métodos Alternativos Min/Max


Los métodos Math.min y Math.max son excelentes para obtener los elementos mínimos y máximos de una colección de elementos. Sin embargo, es importante estar consciente de algunas dificultades que pueden surgir con ellos.

Usarlos con un arreglo que contenga un gran número de elementos (más de ~10⁷ elementos, dependiendo del navegador del usuario) muy probablemente se bloqueará y dará el siguiente mensaje de error:

const arr = Array.from(Array(1000000).keys());
Math.min(arr);
Math.max(arr);

Error de rango no capturado: Se excedió el tamaño máximo de la pila de llamadas


Algunos navegadores podrían devolver un valor NaN en su lugar. Podría ser una mejor manera de manejar los errores, pero aún no resuelve el problema.

En su lugar, para números muy grandes, considera usar algo como esto:

function maxValue(arr) {
  return arr.reduce((max, val) => max > val ? max : val)
}
const arr = Array.from(Array(1000000).keys());
console.log(maxValue(arr)); // 999999

Mejor tiempo de ejecución:

function maxValue(arr) {
  let max = arr[0];

  for (let val of arr) {
    if (val > max) {
      max = val;
    }
  }
  return max;
}
const arr = Array.from(Array(1000000).keys());
console.log(maxValue(arr)); // 999999

Obtener tanto el mínimo como el máximo:

function getMinMax(arr) {
  return arr.reduce(({min, max}, v) => ({
    min: min < v ? min : v,
    max: max > v ? max : v,
  }), { min: arr[0], max: arr[0] });
}
const arr = Array.from(Array(1000000).keys());
console.log(getMinMax(arr)); // {"min":0,"max":999999}

Mejor tiempo de ejecución*:

function getMinMax(arr) {
  let min = arr[0];
  let max = arr[0];
  let i = arr.length;
    
  while (i--) {
    min = arr[i] < min ? arr[i] : min;
    max = arr[i] > max ? arr[i] : max;
  }
  return { min, max };
}
const arr = Array.from(Array(1000000).keys());
console.log(getMinMax(arr)); // {"min":0,"max":999999}

* Probado con 1,000,000 de elementos:
Solo como referencia, el tiempo de ejecución de la 1.ª función (en mi máquina) fue de 15,84 ms frente a la 2.ª función con solo 4,32 ms.

Respuesta #8

Dos formas son más cortas y fáciles:

let arr = [2, 6, 1, 0]

Forma 1:

let max = Math.max.apply(null, arr)

Forma 2:

let max = arr.reduce(function(a, b) {
    return Math.max(a, b);
});

Respuesta #9

.apply se usa a menudo cuando la intención es invocar una función variádica con una lista de valores de argumentos, por ejemplo.

La función Math.max([value1[,value2, ...]]) devuelve el mayor de cero o más números.

Math.max(10, 20); // 20
Math.max(-10, -20); // -10
Math.max(-10, 20); // 20

El método Math.max() no te permite pasar un arreglo. Si tienes una lista de valores de los cuales necesitas obtener el mayor, normalmente llamarías a esta función usando Function.prototype.apply(), por ejemplo.

Math.max.apply(null, [10, 20]); // 20
Math.max.apply(null, [-10, -20]); // -10
Math.max.apply(null, [-10, 20]); // 20

Sin embargo, a partir de ECMAScript 6 puedes usar el operador spread:

El operador de propagación permite que una expresión se expanda en lugares donde se esperan múltiples argumentos (para llamadas a funciones) o múltiples elementos (para literales de arreglos).

Usando el operador spread, lo anterior se puede reescribir de la siguiente manera:

Math.max(...[10, 20]); // 20
Math.max(...[-10, -20]); // -10
Math.max(...[-10, 20]); // 20

Al llamar a una función usando el operador variádico, incluso puedes agregar valores adicionales, por ejemplo.

Math.max(...[10, 20], 50); // 50
Math.max(...[-10, -20], 50); // 50

Bono:

El operador spread te permite usar la sintaxis de literales de array para crear nuevos arrays en situaciones donde en ES5 necesitarías recurrir a código imperativo, usando una combinación de push, splice, etc.

let foo = ['b', 'c'];
let bar = ['a', ...foo, 'd', 'e']; // ['a', 'b', 'c', 'd', 'e']

Respuesta #10

Lo haces extendiendo el tipo Array:

Array.max = function( array ){
    return Math.max.apply( Math, array );
};
Array.min = function( array ){
    return Math.min.apply( Math, array );
}; 

Impulsado desde aquí (por John Resig)

Respuesta #11

Una solución simple para encontrar el valor mínimo en un Array de elementos es usar la función prototipo reduce del Array:

A = [4,3,-9,-2,2,1];
A.reduce((min, val) => val < min ? val : min, A[0]); // returns -9

o usando la función incorporada Math.Min() de JavaScript (gracias @Tenflex):

A.reduce((min,val) => Math.min(min,val), A[0]);

Esto establece min en A[0], y luego verifica para A[1]...A[n] si es estrictamente menor que el min actual. Si A[i] < min, entonces min se actualiza a A[i]. Cuando se han procesado todos los elementos del arreglo, se devuelve min como resultado.

EDITAR: Incluir la posición del valor mínimo:

A = [4,3,-9,-2,2,1];
A.reduce((min, val) => val < min._min ? {_min: val, _idx: min._curr, _curr: min._curr + 1} : {_min: min._min, _idx: min._idx, _curr: min._curr + 1}, {_min: A[0], _idx: 0, _curr: 0}); // returns { _min: -9, _idx: 2, _curr: 6 }

Respuesta #12

Para una solución concisa y moderna, se puede realizar una operación reduce sobre el arreglo, manteniendo un seguimiento de los valores mínimo y máximo actuales, de manera que el arreglo solo se recorra una vez (lo cual es óptimo). Aquí se utiliza la asignación por desestructuración para mayor brevedad.

let array = [100, 0, 50];
let [min, max] = array.reduce(([prevMin,prevMax], curr)=>
   [Math.min(prevMin, curr), Math.max(prevMax, curr)], [Infinity, -Infinity]);
console.log("Min:", min);
console.log("Max:", max);

Para encontrar solo el mínimo o el máximo, podemos realizar una operación de reducción de manera muy similar, pero solo necesitamos llevar un registro del valor óptimo anterior. Este método es mejor que usar apply ya que no causará errores cuando el arreglo sea demasiado grande para la pila.

const arr = [-1, 9, 3, -6, 35];

//Only find minimum
const min = arr.reduce((a,b)=>Math.min(a,b), Infinity);
console.log("Min:", min);//-6

//Only find maximum
const max = arr.reduce((a,b)=>Math.max(a,b), -Infinity);
console.log("Max:", max);//35

Respuesta #13

Otros ya han dado algunas soluciones en las que amplían Array.prototype. Todo lo que quiero en esta respuesta es aclarar si debería ser Math.min.apply( Math, array ) o Math.min.apply( null, array ). Entonces, ¿qué contexto debería usarse, Math o null?

Al pasar null como contexto a apply, entonces el contexto se establecerá por defecto en el objeto global (el objeto window en el caso de los navegadores). Pasar el objeto Math como contexto sería la solución correcta, pero tampoco dañará pasar null. Aquí hay un ejemplo de cuándo null podría causar problemas, al decorar la función Math.max:

// decorate Math.max
(function (oldMax) {
    Math.max = function () {
        this.foo(); // call Math.foo, or at least that's what we want

        return oldMax.apply(this, arguments);
    };
})(Math.max);

Math.foo = function () {
    print("foo");
};

Array.prototype.max = function() {
  return Math.max.apply(null, this); // <-- passing null as the context
};

var max = [1, 2, 3].max();

print(max);

Lo anterior lanzará una excepción porque this.foo se evaluará como window.foo, que es undefined. Si reemplazamos null con Math, las cosas funcionarán como se espera y la cadena "foo" se imprimirá en la pantalla (probé esto usando Mozilla Rhino).

Puedes asumir bastante bien que nadie ha decorado Math.max, así que pasar null funcionará sin problemas.

Respuesta #14

Una manera más de hacerlo:

var arrayMax = Function.prototype.apply.bind(Math.max, null);

Uso :

var max = arrayMax([2, 5, 1]);

Respuesta #15

Me sorprende que nadie haya mencionado la función reduce.

var arr = [1, 10, 5, 11, 2]

var b = arr.reduce(function(previous,current){ 
                      return previous > current ? previous:current
                   });

b => 11
arr => [1, 10, 5, 11, 2]

Respuesta #16

Esto puede adaptarse a tus propósitos.

Array.prototype.min = function(comparer) {

    if (this.length === 0) return null;
    if (this.length === 1) return this[0];

    comparer = (comparer || Math.min);

    var v = this[0];
    for (var i = 1; i < this.length; i++) {
        v = comparer(this[i], v);    
    }

    return v;
}

Array.prototype.max = function(comparer) {

    if (this.length === 0) return null;
    if (this.length === 1) return this[0];

    comparer = (comparer || Math.max);

    var v = this[0];
    for (var i = 1; i < this.length; i++) {
        v = comparer(this[i], v);    
    }

    return v;
}

Respuesta #17

https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Math/max

function getMaxOfArray(numArray) {
  return Math.max.apply(null, numArray);
}

var arr = [100, 0, 50];
console.log(getMaxOfArray(arr))

esto funcionó para mí.

Respuesta #18

Pensé en compartir mi solución simple y fácil de entender.

Para el mínimo:

var arr = [3, 4, 12, 1, 0, 5];
var min = arr[0];
for (var k = 1; k < arr.length; k++) {
  if (arr[k] < min) {
    min = arr[k];
  }
}
console.log("Min is: " + min);

Y para el máximo:

var arr = [3, 4, 12, 1, 0, 5];
var max = arr[0];
for (var k = 1; k < arr.length; k++) {
  if (arr[k] > max) {
    max = arr[k];
  }
}
console.log("Max is: " + max);

Respuesta #19

deja array=[267, 306, 108] deja longest=Math.max(...array);

Respuesta #20

La mejor manera con Math.min(...array) y Math.max(...array)

Respuesta #21

Para grandes arrays (~10⁷ elementos), Math.min y Math.max producen un RangeError (Tamaño máximo de pila excedido) en node.js.

Para arreglos grandes, una solución rápida y sucia es:

Array.prototype.min = function() {
    var r = this[0];
    this.forEach(function(v,i,a){if (v<r) r=v;});
    return r;
};

Respuesta #22

array.sort((a, b) => b - a)[0];

Te da el valor máximo en un arreglo de números.

array.sort((a, b) => a - b)[0];

Te da el valor mínimo en un arreglo de números.

let array = [0,20,45,85,41,5,7,85,90,111];

let maximum = array.sort((a, b) => b - a)[0];
let minimum = array.sort((a, b) => a - b)[0];

console.log(minimum, maximum)

Respuesta #23

Para un arreglo que contiene objetos en lugar de números:

arr = [
  { name: 'a', value: 5 },
  { name: 'b', value: 3 },
  { name: 'c', value: 4 }
]

Puedes usar reduce para obtener el elemento con el valor más pequeño (mínimo)

arr.reduce((a, b) => a.value < b.value ? a : b)
// { name: 'b', value: 3 }

o el valor más grande (máx)

arr.reduce((a, b) => a.value > b.value ? a : b)
// { name: 'a', value: 5 }

Respuesta #24

Además de usar las funciones matemáticas max y min, otra función para usar es la función incorporada sort(): aquí vamos

const nums = [12, 67, 58, 30].sort((x, y) => 
x -  y)
let min_val = nums[0]
let max_val = nums[nums.length -1]

Artículos relacionados