tags:

views:

1674

answers:

3

I have an array of doubles and I want the index of the highest value. These are the solutions that I've come up with so far but I think that there must be a more elegant solution. Ideas?

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();
+6  A: 

I suggest writing your own extension method (edited to be generic with an IComparable<T> constraint.)

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

Note that this returns -1 if the sequence is empty.

A word on the characteristics:

  • This works with a sequence which can only be enumerated once - this can sometimes be very important, and is generally a desirable feature IMO.
  • The memory complexity is O(1) (as opposed to O(n) for sorting)
  • The runtime complexity is O(n) (as opposed to O(n log n) for sorting)

As for whether this "is LINQ" or not: if it had been included as one of the standard LINQ query operators, would you count it as LINQ? Does it feel particularly alien or unlike other LINQ operators? If MS were to include it in .NET 4.0 as a new operator, would it be LINQ?

EDIT: If you're really, really hell-bent on using LINQ (rather than just getting an elegant solution) then here's one which is still O(n) and only evaluates the sequence once:

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

It's hideous, and I don't suggest you use it at all - but it will work.

Jon Skeet
This isn't LINQ Jon
Pascal Paradis
It is Linq to Objects, Pascal.
Will
@Pascal: How do you define LINQ, exactly? To me, one of the nice things about LINQ is that you can add your own operators which work smoothly with the predefined ones. Editing for performance issues.
Jon Skeet
@Jon: Got it! I was out of the track. That being said. Elegant solution!
Pascal Paradis
That's a great answer Jon - thanks. I tend to refer to extension methods like this LINQ but I'm guessing that I'd lose a semantic argument if that's what it came down to.
Guy
I fixed a small bug in the first code fragment. You had it returning index instead of maxIndex.
Guy
Doh! Thanks very much Guy :)
Jon Skeet
+7  A: 
var scoreList = score.ToList();
int topIndex =
    (
      from x
      in score
      orderby x
      select scoreList.IndexOf(x)
    ).Last();

If score wasn't an array this wouldn't be half bad...

Will
I have to vote up Jon; its probably the better solution overall. This is the linq-iest way to do it without writing an extension method, tho.
Will
I have to vote up Will. I like Jon's answer but this seems to come closer to answering the question asked. Ultimately, Guy will let us know which answer is best :)
wcm
Will - I love this answer. From a purest point of view this is probably the "correct" answer but I felt that Jon's answer was what I wanted. Thanks for taking the time to answer the question.
Guy
+1  A: 

Pure LINQ

        int[] nums = {1,2,3,4,6,2,3};
        int i=0;
        int max = nums[0];
        int result = (from num in nums
                      let index = i++
                      where num > max
                      let x = Interlocked.Exchange(ref max, num)
                      select index).Last();
Hasan Khan