Возвращает индекс наибольшего значения в массиве

139

У меня есть это:

var arr = [0, 21, 22, 7];

Каков наилучший способ вернуть индекс наибольшего значения в другую переменную?

Стивен
источник
21
Это не дубликат, прочтите вопрос ... @Dancrumb Как работает SO, я публикую этот вопрос, и в течение десятилетий люди будут его находить, читать и быть благодарными за информацию, которую участники разместили ниже!
Стивен
1
@Stephen, напротив, если вы прочитаете этот FAQ , вы увидите, что субъективные вопросы (например, те, которые начинаются с вопроса «Как лучше ...») категорически не приветствуются. Однако SO - это сообщество, поэтому сообщество должно решить, следует ли закрывать этот вопрос или нет.
Dancrumb
6
Найти наибольшее число в массиве - это не то же самое, что найти индекс наибольшего числа в массиве. Поэтому этот вопрос не является дубликатом той ссылки, опубликованной как дубликат.
Fzs2
15
Это НЕ дубликат или, по крайней мере, не упомянутый вопрос. Я не могу понять отрицательные голоса по этому поводу - сказать, что вы не можете спросить SO, как лучше всего что-то сделать, - смехотворно, и для такого краткого вопроса не требуется пример кода. Как получилось, что упомянутый «дубликат» имеет +30 голосов, а этот параллельный, НО РАЗНЫЙ вопрос, заданный в очень похожем стиле, имеет -3 голоса? Пожалуйста, перечитайте вопрос и удалите помеченные как дубликаты голоса. "index of" - это ключевая фраза, которую нужно указать в заголовке вопроса.
блин
@Dancrumb Текущий совет в stackoverflow.com/help/dont-ask препятствует возникновению субъективных вопросов, только если все ответы одинаково верны. В этом случае «лучший» однозначно можно оценить по исполнению, лаконичности и выразительности.
user234461

Ответы:

165

Это, вероятно, лучший способ, поскольку он надежен и работает на старых браузерах:

function indexOfMax(arr) {
    if (arr.length === 0) {
        return -1;
    }

    var max = arr[0];
    var maxIndex = 0;

    for (var i = 1; i < arr.length; i++) {
        if (arr[i] > max) {
            maxIndex = i;
            max = arr[i];
        }
    }

    return maxIndex;
}

Также есть однострочный:

let i = arr.indexOf(Math.max(...arr));

Он выполняет в два раза больше сравнений, чем необходимо, и RangeError, тем не менее, будет вызывать большие массивы. Я бы придерживался функции.

Ry-
источник
1
Хорошо, эта функция возвращает первый встреченный индекс для наибольшего значения. Допустим, у меня есть несколько индексов с одинаковым наивысшим значением, как мне получить все эти индексы?
ed1nh0
1
@ ed1nh0: простой способ - сделать несколько проходов. Найдите максимум с помощью const max = arr.reduce((m, n) => Math.max(m, n)), тогда индексы максимума равны [...arr.keys()].filter(i => arr[i] === max).
Ry-
[...arr.keys()] выводит ошибку: unexpected token
ed1nh0
@ ed1nh0: На какой браузер / среду вы нацеливаетесь?
Ry-
Хром. Я использую VueJS, и я думаю, что проблема в конфигурации веб-пакета.
ed1nh0
82

Одной строкой и, возможно, быстрее arr.indexOf(Math.max.apply(Math, arr)):

var a = [0, 21, 22, 7];
var indexOfMaxValue = a.reduce((iMax, x, i, arr) => x > arr[iMax] ? i : iMax, 0);

document.write("indexOfMaxValue = " + indexOfMaxValue); // prints "indexOfMaxValue = 2"

Куда:

  • iMax- лучший индекс на данный момент (индекс максимального элемента на первой итерации, iMax = 0потому что второй аргумент reduce()равен 0, мы не можем пропустить второй аргумент reduce()в нашем случае)
  • x - текущий проверенный элемент из массива
  • i - текущий протестированный индекс
  • arr- наш массив ( [0, 21, 22, 7])

О reduce()методе (из «JavaScript: Полное руководство» Дэвида Фланагана):

reduce () принимает два аргумента. Первая - это функция, выполняющая операцию сокращения. Задача этой функции сокращения - каким-то образом объединить или уменьшить два значения в одно значение и вернуть это уменьшенное значение.

Функции, используемые с reduce (), отличаются от функций, используемых с forEach () и map (). Знакомое значение, индекс и значения массива передаются как второй, третий и четвертый аргументы. Первый аргумент - это накопленный к настоящему моменту результат сокращения. При первом вызове функции этот первый аргумент - это начальное значение, которое вы передали в качестве второго аргумента функции reduce (). При последующих вызовах это значение, возвращаемое предыдущим вызовом функции.

Когда вы вызываете reduce () без начального значения, он использует первый элемент массива в качестве начального значения. Это означает, что первый вызов функции сокращения будет иметь первый и второй элементы массива в качестве первого и второго аргументов.

traxium
источник
12
@traxium Хотя ваше объяснение великолепно, для тех, кто меньше занимается функциональным программированием, пример мог бы быть более понятным, если бы мы использовали более описательные переменные. Скажи: arr.reduce((bestIndexSoFar, currentlyTestedValue, currentlyTestedIndex, array) => currentlyTestedValue > array[bestIndexSoFar] ? currentlyTestedIndex : bestIndexSoFar, 0);, которая может быть описана как: итерация массива , начиная с индекса 0 (второй параметр), если currentlyTestedValue выше , чем значение элемента в bestIndexSoFar , а затем вернуть currentlyTestedIndex к следующей итерации в качестве bestIndexSoFar .
niieani
1
@traxium Замечательный ответ. Я также согласен с @niieani Вот реальный пример я реализовал: this.methods.reduce((methodIndex, currentMethod, currentMethodIndex, methods) => currentMethod.price <= methods[methodIndex].price ? currentMethodIndex : methodIndex, 0).
Даниэль
2
@DanielK, ответ с "полными" именами параметров не поместился бы в одну строку stackoverflow. Появится горизонтальная полоса прокрутки и читать фрагмент при горизонтальной прокрутке будет не очень удобно. В любом случае спасибо за предложения. Я отредактировал ответ по-другому.
traxium
@traxium +1 для раствора ФП. Хотя это болезненно сложно для тех, кто только начинает работать с JS, ваше решение также оказывается одним из самых эффективных для решения проблемы OP.
SeaWarrior404
Согласно jsben.ch/ujXlk , другой метод быстрее.
VFDan
48

Вот еще одно решение, если вы используете ES6 с оператором распространения:

var arr = [0, 21, 22, 7];

const indexOfMaxValue = arr.indexOf(Math.max(...arr));
Ланил Марасингхе
источник
6

Если не ошибаюсь, я бы сказал, чтобы написать свою функцию.

function findIndexOfGreatest(array) {
  var greatest;
  var indexOfGreatest;
  for (var i = 0; i < array.length; i++) {
    if (!greatest || array[i] > greatest) {
      greatest = array[i];
      indexOfGreatest = i;
    }
  }
  return indexOfGreatest;
}
Дэн Тао
источник
6

Если вы используете подчеркивание, вы можете использовать этот красивый короткий однострочник:

_.indexOf(arr, _.max(arr))

Сначала он найдет значение самого большого элемента в массиве, в данном случае 22. Затем он вернет индекс, где 22 находится внутри массива, в данном случае 2.

Kevin
источник
6

Другое решение макс с использованием reduce:

[1,2,5,0,4].reduce((a,b,i) => a[0] < b ? [b,i] : a, [Number.MIN_VALUE,-1])
//[5,2]

Это возвращается, [5e-324, -1]если массив пуст. Если вам нужен только индекс, поместите[1] после.

Мин. Через (Изменить на >и MAX_VALUE):

[1,2,5,0,4].reduce((a,b,i) => a[0] > b ? [b,i] : a, [Number.MAX_VALUE,-1])
//[0, 3]
randompast
источник
1

РЕДАКТИРОВАТЬ: Несколько лет назад я дал ответ на этот вопрос, который был грубым, слишком конкретным и слишком сложным. Так что редактирую. Я предпочитаю функциональные ответы выше из-за их аккуратности, но не из-за их удобочитаемости; но если бы я был более знаком с javascript, то они мне тоже могли бы понравиться.

Псевдокод:

Отслеживать индекс, который содержит наибольшее значение. Предположим, что индекс 0 является наибольшим изначально. Сравните с текущим индексом. Обновите индекс с наибольшим значением при необходимости.

Код:

var mountains = [3, 1, 5, 9, 4];

function largestIndex(array){
  var counter = 1;
  var max = 0;

  for(counter; counter < array.length; counter++){
    if(array[max] < array[counter]){
        max = counter;
    }
  }
  return max;
}

console.log("index with largest value is: " +largestIndex(mountains));
// index with largest value is: 3
Росс Стадтман
источник
Спасибо! Пришлось возиться и дальше.
Росс Студтман 08
1
function findIndicesOf(haystack, needle)
{
    var indices = [];

    var j = 0;
    for (var i = 0; i < haystack.length; ++i) {
        if (haystack[i] == needle)
            indices[j++] = i;
    }
    return indices;
}

перейти arrayк haystackи Math.max(...array)к needle. Это даст все максимальные элементы массива, и он будет более расширяемым (например, вам также нужно найти минимальные значения)

Эдвард Карак
источник
1

Если вы создадите копию массива и отсортируете ее по убыванию, первый элемент копии будет самым большим. Затем вы можете найти его индекс в исходном массиве.

var sorted = [...arr].sort((a,b) => b - a)
arr.indexOf(sorted[0])

Сложность по времени составляет O (n) для копии, O (n * log (n)) для сортировки и O (n) для indexOf.

Если вам нужно сделать это быстрее, ответ Ry - O (n).

rodurico
источник
0

 var arr=[0,6,7,7,7];
 var largest=[0];
 //find the largest num;
 for(var i=0;i<arr.length;i++){
   var comp=(arr[i]-largest[0])>0;
      if(comp){
	  largest =[];
	  largest.push(arr[i]);
	  }
 }
 alert(largest )//7
 
 //find the index of 'arr'
 var arrIndex=[];
 for(var i=0;i<arr.length;i++){
    var comp=arr[i]-largest[0]==0;
	if(comp){
	arrIndex.push(i);
	}
 }
 alert(arrIndex);//[2,3,4]

mos wen
источник
-1

Стабильная версия этой функции выглядит так:

// not defined for empty array
function max_index(elements) {
    var i = 1;
    var mi = 0;
    while (i < elements.length) {
        if (!(elements[i] < elements[mi]))
            mi = i;
        i += 1;
    }
    return mi;
}
Питер Стуйфзанд
источник
Что в этом контексте означает «стабильный»?
Ry-
Я предполагаю, что он имел в виду «подходящий»
Икбель