Получить индекс максимального элемента - PullRequest
19 голосов
/ 16 июля 2009

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

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

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

Редактировать: Жаль, что стандартный LINQ не поддерживает эти функции.

Ответы [ 9 ]

35 голосов
/ 16 июля 2009

таким образом:

var maxIndex = foo.IndexOf(foo.Max());
16 голосов
/ 17 июля 2009

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

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

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

  3. Aggregate получит элемент с наибольшим Value;

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

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

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

РЕДАКТ. 1: добавлена ​​проверка пустого списка.

РЕДАКТИРОВАТЬ 2: добавлены предостережения о производительности.

11 голосов
/ 16 июля 2009

Вот пользовательский метод 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;
    }
}
7 голосов
/ 16 июля 2009

Вот как это сделать в одну (длинную) строку, используя 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;
    }
}
3 голосов
/ 16 июля 2009

Я не могу улучшить ответ Джона Скита для общего случая, поэтому я собираюсь получить приз «высокая производительность» в конкретном случае списка целых.

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;
    }
2 голосов
/ 16 июля 2009

Вот метод non-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;
        }

Это хотя бы один проход по списку.

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

Кайл

0 голосов
/ 19 декабря 2013
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, и я нахожу это очень удобным.

0 голосов
/ 19 июня 2013

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

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;
}
0 голосов
/ 16 июля 2009

Использование пользовательской функции, использование Max () и IndexOf () обходится дороже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...