29

I'm new to LINQ. I need to compute new_id as follows:

public class C_Movement
{
  public int id=-1;
  public static ObservableCollection<C_Movement> list=new ObservableCollection<C_Movement>();
  // ...
}

int new_id = (C_Movement.list.Count==0) ? 0 : C_Movement.list.Max(x => x.id)+1;

Is there a LINQ way to compact that expression, so that I don't have to use the ? : structure? The problem is that, when C_Movement.list contains no elements, C_Movement.list.Max(x => x.id) returns null (and I would like it to return -1, instead).

Thank you.

d219
  • 2,275
  • 5
  • 21
  • 29
Telaclavo
  • 2,209
  • 2
  • 15
  • 15
  • 2
    How can it return null when it's returning an integer? – MikeP Mar 30 '12 at 17:49
  • 1
    For future reference, the "? :" structure is actually called *the conditional operator* (in MSDN docs), but most folks call it *the ternary operator.* :) – Mike Hofer Mar 30 '12 at 18:10

4 Answers4

61

DefaultIfEmpty method should help:

int new_id = C_Movement.list.Select(x => x.id).DefaultIfEmpty(-1).Max()+1;
Snowbear
  • 16,263
  • 3
  • 40
  • 63
5

int new_id = C_Movement.list.Max(x => (int?)x.id).GetValueOrDefault(-1) + 1;

where GetValueOrDefault is a method of Nullable<T>.

Vladimir
  • 6,917
  • 4
  • 32
  • 39
  • 6
    It's worth noting that only Max() on collections of nullable types returns null for empty sequence while Max() in general throws exception. – sluki Jul 21 '14 at 16:12
2

How about:

int new_id = 0;

if (C_Movement.list.Any())
    new_id = C_Movement.list.Max(x => x.id) + 1;
Only Bolivian Here
  • 32,571
  • 60
  • 151
  • 250
  • 2
    you don't need an `else` here since you've already set it to `0` – hunter Mar 30 '12 at 17:50
  • That's true, I guess I'm anal about certain things. Bad habits die hard! – Only Bolivian Here Mar 30 '12 at 17:51
  • 2
    The major problem with this version is that it will double enumerate the iterator. The "Any" will be the first iteration and the "Max" will be the second. This implementation should be avoided. – srm Aug 14 '14 at 14:06
-1

Try this

    public static class LinqExtensions
    {
        public static TValue Max<TSource, TValue>(this IEnumerable<TSource> source, Func<TSource, TValue> selector, TValue defaultValueIfEmpty)
            where TValue : IComparable
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (selector == null)
                throw new ArgumentNullException(nameof(selector));
            TValue sum;
            using (IEnumerator<TSource> enumerator = source.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                    return defaultValueIfEmpty;
                sum = selector(enumerator.Current);
                while (enumerator.MoveNext())
                {
                    var num2 = selector(enumerator.Current);
                    if (num2.CompareTo(sum) > 0)
                        sum = num2;
                }
            }
            return sum;
        }

        public static TSource Max<TSource>(this IEnumerable<TSource> source, TSource defaultValueIfEmpty)
            where TSource : IComparable
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            TSource sum;
            using (IEnumerator<TSource> enumerator = source.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                    return defaultValueIfEmpty;
                sum = enumerator.Current;
                while (enumerator.MoveNext())
                {
                    var num2 = enumerator.Current;
                    if (num2.CompareTo(sum) > 0)
                        sum = num2;
                }
            }
            return sum;
        }

    }
Softlion
  • 11,311
  • 10
  • 52
  • 78
  • The generic type constraint `where TValue : IComparable` defeats the purpose of `defaultValueIfEmpty` because a nullable type doesn't implement `IComparable`. Besides that, (1) it's always useful when posting code exceeding a couple of lines to add some explanation, and (2) "Try this" isn't really an answer. – Gert Arnold May 03 '19 at 08:55
  • I meant `Nullable`. My first effort trying your code failed with `int?`. About an explanation. Code itself may be easy to understand, but the process leading to that specific solution is at least as interesting. – Gert Arnold May 04 '19 at 15:38
  • It's the exact same Microsoft source code of Max augmented to have a default value. – Softlion May 04 '19 at 16:37
  • https://github.com/Microsoft/referencesource/blob/master/System.Core/System/Linq/Enumerable.cs Line 1879. It uses a Comparer.Default which implements IComparer. – Softlion May 04 '19 at 16:50
  • That's not the same as a generic type constraint. `Comparer.Default` is an object comparing two `int?` instances. An `int?` itself is not `IComparable`, rendering your function useless for `Nullable` types (and anything else that doesn't implement `IComparable`). – Gert Arnold May 04 '19 at 18:24
  • if the compiler does accept int? as an argument for TValue, it means it implements IComparable. If it does not, then you can not use this method. Either way, the method behave as expected. – Softlion May 06 '19 at 05:54