1

Short of iterating the collection, is there a way to return the double in a set that has the highest absolute value without making the values in the set actually absolute?

double[] vals = new double[] { 2.3, 1.7, -3.8};

vals.Max(v => Math.Abs(v)); // This returns 3.8 instead of -3.8
Wobbles
  • 2,789
  • 1
  • 19
  • 44
  • "iterating the collection". Enumerable.Max iterates a collection. old good `for` loop will be the most performant solution – ASh Jul 08 '17 at 13:03
  • @ASh AFAIK native .net iterations have a performance boost over `for` loops, there is a performance concern at play here that I didn't mention and was wondering if there is some sort of native implementation before I went the route of writing my own `IEnumerable` extension. – Wobbles Jul 08 '17 at 13:07
  • 1
    What is a `native .net iteration` @Wobbles ? – mjwills Jul 08 '17 at 13:47
  • Possible duplicate of [LINQ: How to perform .Max() on a property of all objects in a collection and return the object with maximum value](https://stackoverflow.com/questions/1101841/linq-how-to-perform-max-on-a-property-of-all-objects-in-a-collection-and-ret) – mjwills Jul 08 '17 at 22:53

2 Answers2

5

One approach to consider:

var max = vals
    .OrderByDescending(z => Math.Abs(z))
    .FirstOrDefault();

Alternatively, consider using MoreLinq's MaxBy. It is faster than both my and Samvel's solution, especially for larger sets of inputs.

var max = vals.MaxBy(z => Math.Abs(z));
mjwills
  • 21,750
  • 6
  • 36
  • 59
1

Here are two ways of doing this:

First with LINQ:

double[] vals = new double[] { 2.3, 1.7, -3.8};
var max = vals.Max(x => Math.Abs(x)); 
Console.WriteLine(vals.Where(z => Math.Abs(z) == max).First());

Second with For loop:

int index =0;
for(int i=0;i<vals.Length;i++)
{
    if(Math.Abs(vals[i])>=Math.Abs(vals[index]))
        index=i;
}
Console.WriteLine(vals[index]); 
mjwills
  • 21,750
  • 6
  • 36
  • 59
Samvel Petrosov
  • 6,842
  • 2
  • 16
  • 44
  • Ahh some gold-ol linq kung-fu – Wobbles Jul 08 '17 at 13:10
  • 1
    Having the `vals.Max` **inside** the `Where` clause will mean the input is iterated over more times than you expect. You will want to move that outside the `Where` clause (but check that `vals.Any()` is true beforehand, otherwise the `Max` will throw an exception). – mjwills Jul 08 '17 at 13:25
  • @mjwills it is iterating 2 times as in case if you first select all into anonymouse type, also it will not throw exceptions as there is always the element which we consider to be max – Samvel Petrosov Jul 08 '17 at 13:30
  • By leaving the `Max` inside the `Where` then it calculates the `Max` for every entry (so basically O(n^2)). – mjwills Jul 08 '17 at 13:44
  • @mjwills Does your answer suffer the same flaw? – Wobbles Jul 08 '17 at 13:51
  • I don't use `Max`, so no. I am not saying my solution is better or worse, but it lacks that **specific** flaw. You'd have to profile both solutions (using https://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch(v=vs.110).aspx over multiple iterations with varying size of inputs) to see which is more performant for your context. If your input size is small, almost any solution will be fast. – mjwills Jul 08 '17 at 13:52
  • 1
    @mjwills I have tried all three(Max in, Max out and your one) versions of code with iterations and Stopwatches and the fastest one is the version with Max out. I will update answer now. Thanks – Samvel Petrosov Jul 08 '17 at 14:10
  • @mjwills wow, I have not heard about this LINQ's Extensions Pack. Will save it for future. I think that is the best solution for this answer. – Samvel Petrosov Jul 10 '17 at 08:42