tags:

views:

226

answers:

7

Given such a list:

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

How to obtain the index of the maximum element in the list? In this case, it's 3.

This should be an easy question, but I don't know why I can get this out.

Edit: It's a shame that standard LINQ doesn't ship this functionalities...

+1  A: 

this way :

var maxIndex = foo.IndexOf(foo.Max());
Adinochestva
This is clean and short, but requires up to two full passes through the list to obtain the index. If you need it faster, you should use a for loop and keep track of the index as you go.
280Z28
It's still O(n), and the second pass might not be a full pass. I'd go with this code if performance if not a big issue.
Meta-Knight
A: 

Use a custom function, using Max() and IndexOf() cost more.

Clement Herreman
+1  A: 

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

Kyle Rozendo
+4  A: 

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;
    }
}
Jon Skeet
Looks like the right idea (cursory look, it's late). I'd use `selector` as the name of your second parameter to match the Enumerable.Max() methods.
280Z28
@280Z28: I think you must have been looking at the old version - I got rid of the selector bit...
Jon Skeet
A: 

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;
    }
Matt Howells
+6  A: 

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;
    }
}
LukeH
+2  A: 

Here is a simple and 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;
  1. The !intList.Any() ? -1 : will force a -1 if the list is empty;

  2. The Select will project each int element into an anonymous type with two properties: Value and Index;

  3. The Aggregate will get the element with the highest Value;

  4. Finally, we get the Index of the chosen element.

EDIT 1: empty list check added.

jpbochi