Reputation: 83244
Given such a list:
List<int> intList = new List<int>();
intList.Add(5);
intList.Add(10);
intList.Add(15);
intList.Add(46);
how do you obtain the index of the maximum element in the list? In this case, it's at index 3.
Edit: It's a shame that standard LINQ doesn't ship this functionalities.
Upvotes: 29
Views: 44608
Reputation: 378
Simplified.
int MaxIndex = list.DefaultIfEmpty().Select((v, i) => new { v, i }).Aggregate((a, b) => (a.v > b.v) ? a : b).i;
Upvotes: 0
Reputation: 4406
Here is a simple* and relatively efficient** solution:
int indexMax
= !intList.Any() ? -1 :
intList
.Select( (value, index) => new { Value = value, Index = index } )
.Aggregate( (a, b) => (a.Value > b.Value) ? a : b )
.Index;
The !intList.Any() ? -1 :
will force a -1
if the list is empty;
The Select
will project each int
element into an anonymous type with two properties: Value
and Index
;
The Aggregate
will get the element with the highest Value
;
Finally, we get the Index
of the chosen element.
* Simplicity is relative. The aim here was to reach a balance of readability and still only scan the list once.
** The allocation of lots of new objects during the Select
is probably wasteful. As some people tested, it doesn't perform well for large lists.
EDIT 1: empty list check added.
EDIT 2: added caveats about performance.
Upvotes: 17
Reputation: 423
Here is my solution:
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;
}
Upvotes: 2
Reputation: 269278
Here's how to do it in one (long) line using LINQ, with just a single pass through the collection. It should work for any IEnumerable<int>
, not just lists.
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
);
Here's the non-LINQ equivalent of the above, using a foreach
loop. (Again, just a single pass through the collection, and should work for any 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++;
}
If you know that the collection is an IList<int>
then a plain for
loop is probably the easiest solution:
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;
}
}
Upvotes: 5
Reputation: 41266
I can't improve on Jon Skeet's answer for the general case, so I am going for the 'high performance' prize in the specific case of a list of ints.
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;
}
Upvotes: 3
Reputation: 1499790
Here's a custom LINQ method which I believe does what you want. (I previously had another which does a projection, but you can just call Select to do that, as you only need the index.)
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;
}
}
Upvotes: 15
Reputation: 25277
Here's the non-linq method if you like:
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;
}
This is a single pass through the list at least.
Hope this helps,
Kyle
Upvotes: 1
Reputation: 10536
Use a custom function, using Max() and IndexOf() cost more.
Upvotes: 0