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

Как получить индекс наивысшего значения в массиве с помощью LINQ?

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

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();
4b9b3361

Ответ 1

Я предлагаю написать собственный метод расширения (отредактирован как общий с ограничением 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();

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

Ответ 2

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

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

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

Ответ 3

var scoreList = score.ToList();
int topIndex =
    (
      from x
      in score
      orderby x
      select scoreList.IndexOf(x)
    ).Last();

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

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

Это не единственное решение на основе Aggregate, но это действительно просто однострочное решение.

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

Ответ 7

Наихудшая возможная сложность этого - 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();
}

Ответ 8

Если вы хотите что-то, что выглядит 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;
    }

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