28

Suppose I have two nullable integers:

int? a = 10;
int? b = 20;

I want to take the biggest, non-null value, such that if both values are null, the result is null.

I could write something long-winded such as:

int? max;
if (a == null)
{
    max = b;
}
else if (b == null)
{
    max = a;
}
else
{
    max = a > b ? a : b;
}

This feels a little too clunky (and possibly error-prone) for my liking. What's the simplest way to return the greater value, which also accounts for the possibility of null values?

Paul Turner
  • 38,949
  • 15
  • 102
  • 166

10 Answers10

77

In one line using the null coalescing operator:

int? c = a > b ? a : b ?? a;
Nils O
  • 1,321
  • 9
  • 19
75

This works for any nullable:

Nullable.Compare(a, b) > 0 ? a : b;
romanoza
  • 4,775
  • 3
  • 27
  • 44
  • 2
    defiantly the best solution, so +1, except it obviously won't work for **any** nullable.... – AK_ May 02 '15 at 11:02
  • 4
    I had too look up the behavior of `Nullable.Compare` to see what it did. I find this solution less readable than most (all) of the others as it required looking up documentation to understand. But, I'm sure different people have different things memorized, so perhaps that's just me. – Rick May 04 '15 at 19:41
61

These lines show the necessary logic with a small trick:

if (a == null) return b; // handles b== null also
if (b == null) return a;
// now a!=null, b!=null
return Math.Max(a.Value, b.Value);

or in one line using ?: (exactly the same logic)

 return a == null ? b : b == null ? a : Math.Max(a.Value, b.Value);


Edit

While the answer above is interesting for educational purposes it is not the recomended way to solve this problem. The recomended way is to not reinvent the wheel instead find the matching wheel:

As @roman pointed out there exists a Nullable.Compare() method which makes this much more readable:

return Nullable.Compare(a, b) > 0 ? a : b;
DrKoch
  • 9,556
  • 2
  • 34
  • 43
44

This is a good place for the Null coalescing operator ??.
It returns the first value if the value is not null, otherwise it returns the second value.

int? c = a > b ? a : b ?? a;

Proof here

Using the fact that the comparison operator will return false if either value is null, the expression will give you the desired result:

a        | b        || a>b | a   | b??a | a>b ? a : b??a
--------------------||----------------------------------
> b      | NOT NULL ||  T  | a   | --   | a
≤ b      | NOT NULL ||  F  | --  | b    | b
NOT NULL | NULL     ||  F  | --  | a    | a
NULL     | NOT NULL ||  F  | --  | b    | b
NULL     | NULL     ||  F  | --  | NULL | NULL
Kenogu Labz
  • 1,094
  • 1
  • 9
  • 20
Rick
  • 1,240
  • 14
  • 21
24

A short version is:

var result = new[] { a, b }.Max();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user287107
  • 9,286
  • 1
  • 31
  • 47
  • 4
    Although an answer, would you really want to allocate a single valued array of an anonymous type just to find out which is int is bigger? – Yuval Itzchakov May 01 '15 at 11:13
  • 15
    yes :) the main goal of C# is to write safe and short code, not high performance code. the compiler does the rest. here the compiler creates a int? array, no anonymous type. the performance is about factor 2 worse compared to the other here presented methods. anyway if you want to write high performance code, try to avoid nullable types. – user287107 May 01 '15 at 11:33
  • I really appreciate this answer, as it is both short and creative, still I do not think that safe and short code has to suffer reduced performance (in general). If you need to handle a case where you must use nullable types (with the performance drawback), it is not a good enough reason to forget about performance at all. So, I would disagree with you when you say C# is not to write high-performance code. You may not be able match the speed of native code, but if taken seriously, performance can be significantly improved with some best-practices and compiler-optimization friendly code. – Ivaylo Slavov May 04 '15 at 19:13
8

How about this

 private int? Greater(int? a, int? b)
 {
   if(a.HasValue && b.HasValue)
    return a > b ? a : b;

   if(a.HasValue)
     return a;
   if(b.HasValue)
     return b;

   return null;
  }

or more concise:

 private int? Greater(int? a, int? b)
 {
   if(a.HasValue && b.HasValue)
    return a > b ? a : b;

   return a.HasValue ? a : b;
  }
Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
  • comparisons with null return false https://msdn.microsoft.com/en-us/library/2cf62fcy.aspx#sectionToggle4 – Rick May 01 '15 at 18:04
4
!b.HasValue || a > b ? a : b

If b is null (!b.HasValue) then a is always going to be the correct answer.

If b is not null but a is, then a > b will be false, and b will be the correct answer.

Otherwise it's the same a > b ? a : b that non-nullable integers would have.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
4

How about making a method capable of handling as many nullable values as you have:

public static int? GetLargestNonNull(params int?[] values)
{
    IEnumerable<int?> nonNullValues = values.Where(v => v.HasValue);

    if (nonNullValues.Any())
    {
        return nonNullValues.Select(v => v.Value).Max();
    }

    return null;
}

And use like:

int? result = GetLargestNonNull(a, b);

Except this is also capable of handling:

int? result = GetLargestNonNull(a, b, c, d, e, f);

Or you could change the method parameter to accept a List if you are working with values you've received from another source.

David Spence
  • 7,999
  • 3
  • 39
  • 63
  • 4
    I would much rather take 2 minutes to write this so the rest of my code reads well. Who wants to deal with trying to understand someone else's complicated ternary operators :) – David Spence May 02 '15 at 09:20
1

I would like to add that the one-line solutions here are good. But to demystify the code a little add a brackets around the null coalescing operator

private int? Max(int? a, int? b)
{
    return a > b ? a : (b ?? a);
    //returns a if bigger else non null prefering b
}

Returns a if it is bigger, else return b ?? a - returns non null (or null if both null) prefering b

Alby
  • 321
  • 2
  • 6
0

Here is a very intuitive and readable solution. This will work for any number of values as well as any nullable struct such as say, int? or DateTime?

Also, if all values are null then it returns null.

 public static T? GetGreatestOrDefault<T>(IEnumerable<T?> values) where T : struct
    {
        var any = values.Any(a => a.HasValue);

        if (!any) return null;

        var firstOrDefault = values.Where(v => v.HasValue)
            .Select(v => v.Value)
            .OrderByDescending(o => o)
            .FirstOrDefault();

        return firstOrDefault;
    }

or you might want to do the following:

  public static T? GreatestOrDefault<T>(this IEnumerable<T?> values) where T : struct
        {
            var any = values.Any(a => a.HasValue);

            if (!any) return null;

            var firstOrDefault = values.Where(v => v.HasValue).Max();

            return firstOrDefault;
        }
Phil C
  • 3,687
  • 4
  • 29
  • 51
  • If you're going to use `IEnumerable`, wouldn't `Max()` over the initial collection be a far simpler method? – Paul Turner May 21 '18 at 15:41
  • No because Max() does not work with DateTime and several other structs. – Phil C May 21 '18 at 15:42
  • You can test this for yourself with a few lines of code; `Max()` works exactly as expected with `DateTime?` values. Internally, it just uses the default comparison method. – Paul Turner May 21 '18 at 15:52
  • Check out this answer: https://stackoverflow.com/a/29985658/138578 It's the simplest way to use `Max()` – Paul Turner May 22 '18 at 07:52
  • @PaulTurner Although it works: 1) It's not very intuitive; and 2) it's not that readable for inexperienced developers to read at a glance. In short, not every developer would easily be able to do a dry code review of it, which could be costly to the company. – Phil C May 22 '18 at 13:30