LINQ: Methods returning single values

Over the last few weeks I have been writing about the basics of LINQ, and how we can use it to organize collections of items.

Today I would like to take a look at a special class of LINQ methods: those returning single elements or values, instead of an entire collection.

If you are interested in more of my thoughts on LINQ, make sure to check out the full lists of posts.

First(), Last()

The best aspect of LINQ is the ease with which it can handle large collections and allows us to express complex operations on them with very little code.

However, sometimes we actually have to deal with individual items in a collection. Depending on our needs, there are many ways to extract single items. The extension methods First() and Last() are two of the easiest and most common.

As their name suggests, these methods return the first and last item of a collection respectively.

var numbers = new int[] { 2, 3, 5, 11, 7 };

var a = numbers.First(); // a == 2
var b = numbers.Last(); // b == 7

If we are dealing with an array or a list, it is easy enough to use the indexer to get the first or last value ourselves. For other collections however – of if the type of collection is unknown to us – using these methods is an easy and clear alternative to more complicated code.

Conditional First(), Last()

Note that both methods come with a second overload that takes a Func<TSource, bool> as parameter, where TSource is the type of item in the collection. This allows us to filter the collection and get the first and last elements adhering to whatever criteria we need.

var numbers = new int[] { 2, 3, 5, 11, 7 };

var a = numbers.First(n => n % 2 == 1);
    // first odd number: a == 3

var b = numbers.Last(n => n < 6);
    // last number less than six: b == 5

As with the rest of LINQ, we can pass any delegate as the Func parameter, as long as it has the right type. In most cases, using a lambda function is both the most concise and the clearest option.

FirstOrDefault(), LastOrDefault()

An important detail of the First() and Last() extension method is that they will throw an exception, if called on an empty sequence, or if none of the items in the sequence fulfil the given predicate.

If we are fine with those cases occurring, we can use FirstOrDefault() or LastOrDefault() to avoid that exception, and instead return the default value of the item type. For reference types this is null, and for value types it is the default 0-initialised value.

var numbers = new int[] { 2, 3, 5, 11, 7 };
var words = new string[] { "hello", "world" };
var empty = new string[0];

var a = numbers.FirstOrDefault(n => n > 20);
    // none found: a == 0

var b = words.LastOrDefault(w => w.Length < 3);
    // none found: b == null

var c = empty.FirstOrDefault();
    // empty sequence: c == null

Otherwise these methods behave exactly like First() and Last().

Min(), Max()

LINQ’s Min() and Max() functions are equally easy to understand, compared to the previous methods. In essence, they can be used to extract the minimum and maximum values out of a sequence.

var numbers = new int[] { 2, 3, 5, 11, 7 };

var min = numbers.Min(); // min == 2
var max = numbers.Max(); // max == 11

These methods will work as expected on any sequence of the types int, long, float, double, decimal and their nullable counterparts.

In addition, it will also work fine for types that implement the IComparable<T> or IComparable interfaces.

Note that all overloads of these methods taking value types will throw an exception if called on an empty sequence. For reference or nullable types, they will return null however.

Selected Min(), Max()

Similar to the methods above, the Min() and Max() methods also have overloads taking a Func predicate to specify how the minimum or maximum should be calculated.

var words = new string[] { "one", "two", "three" };

var l = words.Max(w => w.Length); // l == 4 (length of "three")

Note however, that – unlike First() and Last()Min() and Max() do not return the actual item of the sequence that has the minimum or maximum key selected by the given delegate.

In other words, First() and Last() use their delegates like Where() whereas Min() and Max() use them like Select().

var words = new string[] { "one", "two", "three" };

words.Max(w => w.Length);
// equals
words.Select(w => w.Length).Max();

words.First(w => w.Length == 3);
// equals
words.Where(w => w.Length == 3).First();

If you find yourself in need of extracting the largest element from a list – as determined by one of its properties – there unfortunately is no easy method in LINQ for that.

However, feel free to check out my own implementation of MinBy() and MaxBy() methods – and a full write-up on them, doing just that.

As a small spoiler: an easy implementation of those methods uses the LINQ extension method Aggregate(). Strictly speaking this method belongs into this post as being one of the methods not returning a collection. However, we are skipping it today for brevity.

Sum, Average

After Min() and Max(), the Sum() and Average() methods of LINQ will hardly be surprising: they calculate the sum and average of all items in a list, respectively.

What is notable however, is how Sum() always returns a value of the same type as the type summed, while Average() always returns a double, to be able to express fractions more accurately.

var numbers = new int[] { 1, 2, 3 };
var words = new string[] { "two", "four" };

var a = numbers.Sum(); // a == 6
var b = numbers.Average(); // b == 2.0

var c = words.Sum(w => w.Length);
    // c == 7
var d = words.Average(w => w.Length);
    // d == 3.5

Note that with very large collections, floating point accuracy might lead to inaccurate results when using these methods. Within the range of hundreds or thousands of items, and often even more, this should not present any issues however.

Conclusion

This post has given an overview of most of the methods of LINQ that do not return a collection, but only a single item.

I hope you have found this useful. Make sure to let me know what you think in the comments below, especially if there is a particular topic you would like me to cover next.

Enjoy the pixels!

Leave a Reply