Подтвердить что ты не робот

Получить индекс максимального элемента

Учитывая такой список:

        List<int> intList = new List<int>();
        intList.Add(5);
        intList.Add(10);
        intList.Add(15);
        intList.Add(46);

как вы получаете индекс максимального элемента в списке? В этом случае он находится в индексе 3.

Изменить: стыдно, что стандартный LINQ не передает эту функциональность.

4b9b3361

Ответ 1

Вот простое * и относительно эффективное ** решение:

int indexMax
    = !intList.Any() ? -1 :
    intList
    .Select( (value, index) => new { Value = value, Index = index } )
    .Aggregate( (a, b) => (a.Value > b.Value) ? a : b )
    .Index;
  • !intList.Any() ? -1 : заставит -1, если список пуст;

  • Select будет проецировать каждый элемент int в анонимный тип с двумя свойствами: Value и Index;

  • Aggregate получит элемент с наивысшим Value;

  • Наконец, мы получим Index выбранного элемента.

* Простота относительно. Цель здесь заключалась в том, чтобы достичь баланса читаемости и по-прежнему проверять только один раз.

** Распределение множества новых объектов во время Select, вероятно, является расточительным. Как некоторые люди тестировали, он не работает хорошо для больших списков.

EDIT 1: добавлен пустой список.

EDIT 2: добавлено предупреждение о производительности.

Ответ 2

следующим образом:

var maxIndex = foo.IndexOf(foo.Max());

Ответ 3

Здесь пользовательский метод LINQ, который, как я считаю, делает то, что вы хотите. (У меня раньше был другой, который выполняет проекцию, но вы можете просто вызвать Select, чтобы сделать это, поскольку вам нужен только индекс.)

public static int MaxIndex<T>(this IEnumerable<T> source)
{
    IComparer<T> comparer = Comparer<T>.Default;
    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            throw new InvalidOperationException("Empty sequence");
        }
        int maxIndex = 0;
        T maxElement = iterator.Current;
        int index = 0;
        while (iterator.MoveNext())
        {
            index++;
            T element = iterator.Current;
            if (comparer.Compare(element, maxElement) > 0)
            {
                maxElement = element;
                maxIndex = index;
            }
        }
        return maxIndex;
    }
}

Ответ 4

Здесь, как это сделать в одной (длинной) строке, используя LINQ, всего за один проход через коллекцию. Он должен работать для любых IEnumerable<int>, а не только для списков.

int maxIndex = intList
    .Select((x, i) => new { Value = x, Index = i })
    .Aggregate
        (
            new { Value = int.MinValue, Index = -1 },
            (a, x) => (a.Index < 0) || (x.Value > a.Value) ? x : a,
            a => a.Index
        );

Здесь эквивалент не LINQ выше, используя цикл foreach. (Опять же, только один проход через коллекцию и должен работать для любого IEnumerable<int>.)

int maxIndex = -1, maxValue = int.MinValue, i = 0;
foreach (int v in intList)
{
    if ((maxIndex < 0) || (v > maxValue))
    {
        maxValue = v;
        maxIndex = i;
    }
    i++;
}

Если вы знаете, что коллекция является IList<int>, то простой цикл for, вероятно, является самым простым решением:

int maxIndex = -1, maxValue = int.MinValue;
for (int i = 0; i < intList.Count; i++)
{
    if ((maxIndex < 0) || (intList[i] > maxValue))
    {
        maxValue = intList[i];
        maxIndex = i;
    }
}

Ответ 5

Я не могу улучшить ответ Jon Skeet для общего случая, поэтому я собираюсь получить приз "высокой производительности" в конкретном случае списка int.

public static class Extensions
{
    public static int IndexOfMaximumElement(this IList<int> list)
    {
        int size = list.Count;

        if (size < 2)
            return size - 1;

        int maxValue = list[0];
        int maxIndex = 0;

        for (int i = 1; i < size; ++i)
        {
            int thisValue = list[i];
            if (thisValue > maxValue)
            {
                maxValue = thisValue;
                maxIndex = i;
            }
        }

        return maxIndex;
    }

Ответ 6

Если вам нужен не-linq-метод:

private int ReturnMaxIdx(List<int> intList)
        {
            int MaxIDX = -1;
            int Max = -1;

            for (int i = 0; i < intList.Count; i++)
            {
                if (i == 0)
                {
                    Max = intList[0];
                    MaxIDX = 0;
                }
                else
                {
                    if (intList[i] > Max)
                    {
                        Max = intList[i];
                        MaxIDX = i;
                    }
                }
            }

            return MaxIDX;
        }

Это единственный проход по списку.

Надеюсь, что это поможет,

Кайл

Ответ 7

Использовать пользовательскую функцию, используя Max() и IndexOf(), больше.

Ответ 8

Вот мое решение:

public static int IndexOfMax(this IList<int> source)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (source.Count == 0)
        throw new InvalidOperationException("List contains no elements");

    int maxValue = source[0];
    int maxIndex = 0;
    for (int i = 1; i < source.Count; i++)
    {
        int value = source[i];
        if (value > maxValue)
        {
            maxValue = value;
            maxIndex = i;
        }
    }
    return maxIndex;
}

Ответ 9

public static class Extensions
{
    public static int MaxIndex<T>(this IEnumerable<T> TSource)
    {
        int i = -1;
        using (var iterator = TSource.GetEnumerator())
            while (iterator.MoveNext())
                i++;
        return i;
    }
}

Вот моя проблема в этой проблеме. Я вернул -1 вместо исключения исключения, потому что это то, что делает функция FindIndex, и я считаю это очень удобным.