5

I have a generic GetMinimum method. It accepts array of IComparable type (so it may be string[] or double[]). in the case of double[] how can I implement this method to ignore the double.NaN values? (I'm looking for good practices)

when I pass this array

double[] inputArray = { double.NaN, double.NegativeInfinity, -2.3, 3 };

it returns the double.NaN!

public T GetMinimum<T>(T[] array) where T : IComparable<T>
{
    T result = array[0];
    foreach (T item in array)
    {
        if (result.CompareTo(item) > 0)
        {
            result = item;
        }
    }
    return result;
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
Hossein Narimani Rad
  • 31,361
  • 18
  • 86
  • 116

3 Answers3

6

Since both NaN < x and NaN > x will always be false, asking for the minimum of a collection that can contain NaN is simply not defined. It is like dividing by zero: there is no valid answer.

So the logical approach would be to pre-filter the values. That will not be generic but that should be OK.

 var results = inputArray.EliminateNaN().GetMinimum();

Separation of concerns: the filtering should not be the responsibility (and burden) of GetMinimum().

H H
  • 263,252
  • 30
  • 330
  • 514
1

You can't from inside the method. The reason is you have no idea what T can be from inside the method. May be you can by some little casting, but ideally this should be your approach:

public T GetMinimum<T>(T[] array, params T[] ignorables) where T : IComparable<T>
{
    T result = array[0]; //some issue with the logic here.. what if array is empty
    foreach (T item in array)
    {
        if (ignorables.Contains(item)
            continue;

        if (result.CompareTo(item) > 0)
        {
            result = item;
        }
    }
    return result;
}

Now call this:

double[] inputArray = { double.NaN, double.NegativeInfinity, -2.3, 3 };
GetMinimum(inputArray, double.NaN);

If you're sure there is only item to be ignored, then you can take just T as the second parameter (perhaps as an optional parameter).

Or otherwise in a shorter approach, just:

inputArray.Where(x => !x.Equals(double.NaN)).Min();
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • are you sure it doesn't return double.NaN? – Hossein Narimani Rad Jan 21 '13 at 19:07
  • That won't quite work -- calling `ignorables.Contains()` won't do the trick because `double.NaN.Equals(double.NaN)` will return `false`. You need to handle the special case with `double.IsNaN()`. – Jeremy Todd Jan 21 '13 at 19:07
  • @HosseinNarimaniRad it may return, thats why i said the logic inside is not so sound. But editing that wasn't my main priority – nawfal Jan 21 '13 at 19:09
  • @nawfal Huh, I stand corrected. From MSDN: "If two Double.NaN values are tested for equality by calling the Equals method, the method returns true. However, if two NaN values are tested for equality by using the equality operator, the operator returns false." That seems kinda weird! – Jeremy Todd Jan 21 '13 at 19:24
-1

According to this Q&A, it's complicated: Sorting an array of Doubles with NaN in it

Fortunately, you can hack around it:

if( item is Single || item is Double ) {
    Double fitem = (Double)item;
    if( fitem == Double.NaN ) continue;
}
Community
  • 1
  • 1
Dai
  • 141,631
  • 28
  • 261
  • 374
  • 2
    This won't quite work, because `fitem == Double.NaN` will always evaluate to false, even when `fitem` actually is `Double.NaN`. You have to use `Double.IsNaN(fitem)` (or `fitem.Equals(Double.NaN)`, as I discovered). – Jeremy Todd Jan 21 '13 at 19:28