Как получить индекс самого высокого значения в массиве с помощью LINQ? - PullRequest
21 голосов
/ 20 января 2009

У меня есть массив значений типа double, и я хочу, чтобы индекс имел наибольшее значение. Это решения, которые я придумала до сих пор, но я думаю, что должно быть более элегантное решение. Идеи?

double[] score = new double[] { 12.2, 13.3, 5, 17.2, 2.2, 4.5 };
int topScoreIndex = score.Select((item, indx) => new {Item = item, Index = indx}).OrderByDescending(x => x.Item).Select(x => x.Index).First();

topScoreIndex = score.Select((item, indx) => new {Item = item, Index = indx}).OrderBy(x => x.Item).Select(x => x.Index).Last();

double maxVal = score.Max();
topScoreIndex = score.Select((item, indx) => new {Item = item, Index = indx}).Where(x => x.Item == maxVal).Select(x => x.Index).Single();

Ответы [ 8 ]

41 голосов
/ 20 января 2009

Я предлагаю написать свой собственный метод расширения (отредактированный, чтобы быть универсальным с ограничением IComparable<T>.)

public static int MaxIndex<T>(this IEnumerable<T> sequence)
    where T : IComparable<T>
{
    int maxIndex = -1;
    T maxValue = default(T); // Immediately overwritten anyway

    int index = 0;
    foreach (T value in sequence)
    {
        if (value.CompareTo(maxValue) > 0 || maxIndex == -1)
        {
             maxIndex = index;
             maxValue = value;
        }
        index++;
    }
    return maxIndex;
}

Обратите внимание, что возвращается -1, если последовательность пуста.

Слово по характеристикам:

  • Это работает с последовательностью, которая может быть перечислена только один раз - иногда это может быть очень важным и, как правило, является желательной функцией IMO.
  • Сложность памяти O (1) (в отличие от O (n) для сортировки)
  • Сложность во время выполнения O (n) (в отличие от O (n log n) для сортировки)

Что касается того, является ли это "LINQ" или нет: если бы оно было включено в качестве одного из стандартных операторов запросов LINQ, вы бы посчитали его как LINQ? Чувствует ли это себя особенно чуждым или непохожим на других операторов LINQ? Если бы MS включила его в .NET 4.0 в качестве нового оператора, это было бы LINQ?

РЕДАКТИРОВАТЬ: Если вы действительно, действительно, одержимы использованием LINQ (вместо того, чтобы просто получить элегантное решение), то вот тот, который все еще O (n) и оценивает последовательность только один раз:

int maxIndex = -1;
int index=0;
double maxValue = 0;

int urgh = sequence.Select(value => {
    if (maxIndex == -1 || value > maxValue)
    {
        maxIndex = index;
        maxValue = value;
    }
    index++;
    return maxIndex;
 }).Last();

Это отвратительно, и я не советую вам вообще его использовать - но оно будет работать.

36 голосов
/ 12 февраля 2012

Мех, зачем делать это слишком сложно? Это самый простой способ.

var indexAtMax = scores.ToList().IndexOf(scores.Max());

Да, вы можете создать метод расширения, чтобы использовать меньше памяти, но если вы не имеете дело с огромными массивами, вы никогда не заметите разницу.

14 голосов
/ 20 января 2009
var scoreList = score.ToList();
int topIndex =
    (
      from x
      in score
      orderby x
      select scoreList.IndexOf(x)
    ).Last();

Если бы score не был массивом, это не было бы наполовину плохо ...

4 голосов
/ 22 октября 2011

MoreLinq - это библиотека, которая предоставляет эту функциональность и многое другое.

2 голосов
/ 25 ноября 2011

У меня была эта проблема сегодня (чтобы получить индекс в массиве пользователей, которые имели самый высокий возраст), и я сделал это следующим образом:

var position = users.TakeWhile(u => u.Age != users.Max(x=>x.Age)).Count();

Это было в классе C #, так что это решение noob, я уверен, что ваши лучше:)

1 голос
/ 13 октября 2015

Это не единственное решение на основе агрегатов, но на самом деле это просто однолинейное решение.

double[] score = new double[] { 12.2, 13.3, 5, 17.2, 2.2, 4.5 };

var max = score.Select((val,ix)=>new{val,ix}).Aggregate(new{val=-1.0,ix=-1},(z,last)=>z.val>last.val?z:last);

Console.WriteLine ("maximum value is {0}", max.val );
Console.WriteLine ("index of maximum value is {0}", max.ix );
0 голосов
/ 25 февраля 2013

Если вам нужно что-то похожее на LINQy, поскольку оно чисто функциональное, то приведенный выше ответ Джона Скитса можно переписать так:

public static int MaxIndex<T>(this IEnumerable<T> sequence) where T : IComparable<T>
    {
        return sequence.Aggregate(
            new { maxIndex = -1, maxValue = default(T), thisIndex = 0 },
            ((agg, value) => (value.CompareTo(agg.maxValue) > 0 || agg.maxIndex == -1) ?
                             new {maxIndex = agg.thisIndex, maxValue = value, thisIndex = agg.thisIndex + 1} :
                             new {maxIndex = agg.maxIndex, maxValue = agg.maxValue, thisIndex = agg.thisIndex + 1 })).
            maxIndex;
    }

Это имеет ту же вычислительную сложность, что и другой ответ, но более расточительно с памятью, создавая промежуточный ответ для каждого элемента перечисляемого.

0 голосов
/ 10 апреля 2012

Наихудшей из возможных сложностей является O (2N) ~ = O (N), но для этого необходимо два раза перечислить коллекцию.

 void Main()
{
    IEnumerable<int> numbers = new int[] { 1, 2, 3, 4, 5 };

    int max = numbers.Max ();
    int index = -1;
    numbers.Any (number => { index++; return number == max;  });

    if(index != 4) {
        throw new Exception("The result should have been 4, but " + index + " was found.");
    }

    "Simple test successful.".Dump();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...