Как получить индекс наивысшего значения в массиве с помощью 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();
Ответы
Ответ 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;
}
Это имеет ту же вычислительную сложность, что и другой ответ, но более расточительно с памятью, создавая промежуточный ответ для каждого элемента перечисляемого.