33

I have a method that returns an ILookup. In some cases I want to return an empty ILookup as an early exit. What is the best way of constructing an empty ILookup?

marc_s
  • 675,133
  • 158
  • 1,253
  • 1,388
Mike Q
  • 21,350
  • 19
  • 80
  • 124

8 Answers8

53

Further to the answers from mquander and Vasile Bujac, you could create a nice, straightforward singleton-esque EmptyLookup<K,E> class as follows. (In my opinion, there doesn't seem much benefit to creating a full ILookup<K,E> implementation as per Vasile's answer.)

var empty = EmptyLookup<int, string>.Instance;

// ...

public static class EmptyLookup<TKey, TElement>
{
    private static readonly ILookup<TKey, TElement> _instance
        = Enumerable.Empty<TElement>().ToLookup(x => default(TKey));

    public static ILookup<TKey, TElement> Instance
    {
        get { return _instance; }
    }
}
Community
  • 1
  • 1
LukeH
  • 242,140
  • 52
  • 350
  • 400
  • Thanks to mquander and Vasile but I think this merge of the two solutions is the simplest/best. – Mike Q Jul 26 '11 at 07:21
20

There's no built-in, so I'd just write an extension method that runs something along the lines of new T[0].ToLookup<K, T>(x => default(K));

I strongly doubt returning null would be more correct here. It's almost never the case that you want to return null from a method which returns a collection (as opposed to an empty collection.) I could not possibly disagree more with people who are suggesting that.

mqp
  • 64,209
  • 13
  • 90
  • 122
  • The semantics of the return value need to be considered. For example, a search that returns no results should return an empty collection, but a search that has (for example) failed or had invalid parameters should return `null`. – Adam Maras Jul 25 '11 at 19:55
  • 3
    Well, a search that has invalid parameters (as in, invalid arguments to a method, I presume) should probably throw. But I take your meaning. It's certainly common that a method will return an empty collection, though, and it would usually be a mess to return null in that case. – mqp Jul 25 '11 at 19:57
  • You are indeed correct regarding throwing upon error. However, I've worked with business applications where, for example, certain external systems would become unavailable during certain times of day; in such cases, queries and requests would fail, but it's not an "exceptional" case, as it was expected behavior. – Adam Maras Jul 25 '11 at 19:58
  • @mquander - Totally agree about null. Empty something is almost always the better option. – Mike Q Jul 26 '11 at 07:19
  • I agree about throwing instead of returning null - throw a useful exception (don't strip information!) and save yourself a future runtime debugging headache. – jocull Nov 01 '17 at 22:03
8

You can create a singleton class for empty lookups.

using System.Linq;

public sealed class EmptyLookup<T, K> : ILookup<T, K> 
{
        public static readonly EmptyLookup<T, K> Instance { get; }
            = new EmptyLookup<T, K>();

        private EmptyLookup() { }

        public bool Contains(T key) => false;

        public int Count => 0;

        public IEnumerable<K> this[T key] => Enumerable.Empty<K>();

        public IEnumerator<IGrouping<T, K>> GetEnumerator()
          => Enumerable.Empty<IGrouping<K, V>>().GetEnumerator();

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
 }

then you can write code like this:

var x = EmptyLookup<int, int>.Instance;

The benefit of creating a new class is that you can use the "is" operator and check for type equality:

if (x is EmptyLookup<,>) {
 // ....
}
Vasea
  • 4,703
  • 2
  • 24
  • 30
  • This is probably the best answer, strictly speaking. – mqp Jul 25 '11 at 19:56
  • Thanks, looks good. I would suggest that [] should throw KeyNotFoundException rather than NotImplementedException. – Mike Q Jul 26 '11 at 07:18
  • 2
    Actually the .NET implementation of `Lookup` returns an empty sequence if _key_ is not found in the collection – Vasea Mar 28 '13 at 08:35
2

Based on LukeH answer, I'd create a static class Lookup with an Empty<TKey, TElement> method. In this way you could use is just the same way as Enumerable.Empty<T>.

public static class Lookup
{
    public static ILookup<TKey, TElement> Empty<TKey, TElement>()
        => Enumerable.Empty<TElement>().ToLookup(x => default(TKey));
}

Example Usage: Lookup.Empty<string, string>()

  • 1
    I like this API. But I think it would be better to cache the result of this method in the same way `Enumerable.Empty` does. As shown in https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,4d026772cf3399f5 it is calling to a static property in an internal class. – Mariano Desanze Mar 29 '19 at 22:21
1

Create an empty list, then execute ToLookup() on it, like this:

List<Point> items = new List<Point>();
ILookup<int, int> lookup = items.ToLookup(p => p.X, p => p.Y);

Good luck!

Michael Ames
  • 2,577
  • 1
  • 14
  • 21
0

Thanks @mqp for the good idea. I can propose several extension methods based on that approach:

public static class IEnumerableExtensions
{
    public static ILookup<TKey, TElement> ToEmptyLookup<TKey, TElement>(this IEnumerable<TElement> elements) => new TElement[0].ToLookup(k => default(TKey));
    public static ILookup<TKey, TElement> ToEmptyLookup<TKey, TElement>(this IDictionary<TKey, TElement> elements) => new TElement[0].ToLookup(k => default(TKey));
    public static ILookup<TKey, TElement> ToEmptyLookup<TKey, TElement>(this IGrouping<TKey, TElement> elements) => new TElement[0].ToLookup(k => default(TKey));
    public static ILookup<TKey, TElement> ToEmptyLookup<TKey, TElement>(this IEnumerable<ILookup<TKey, TElement>> elements) => new TElement[0].ToLookup(k => default(TKey));
}
Andrey Burykin
  • 612
  • 9
  • 21
-1

Or something more in the spirit of LINQ:

    public static class Utility
{

    public static ILookup<TKey, TElement> EmptyLookup<TKey, TElement>(Func<TKey, TKey> keySelector,
                                                                      Func<TKey, TElement> elementSelector)
    {
        return Enumerable.Empty<TKey>().ToLookup(keySelector, elementSelector);
    }

}
markolaban
  • 117
  • 1
  • 6
-4

You can return null

or Exception

But you should note it in class comment

Added:+ This is more obvious than some extension method

Alexander Molodih
  • 1,840
  • 2
  • 19
  • 30
  • 5
    I totally disagree. If a function returns a lookup mapping some `K`s to `T`s, and it so happens that there are no `K`s or `T`s, it's dead obvious that the function should return an empty lookup. It doesn't make any sense to return null or throw an exception, and suddenly have to check for it everywhere, when it would have just worked out of the box if you had returned an empty lookup. – mqp Jul 25 '11 at 20:14
  • "ILookup as an early exit" - This is a typical case when you should throw an exception. – Alexander Molodih Jul 26 '11 at 04:43
  • If you place comment aboute exception, all other developers see it in style sence. And process that exception. – Alexander Molodih Jul 26 '11 at 04:45
  • + This is more obvious than some extension method – Alexander Molodih Jul 26 '11 at 04:52
  • I agree with mquander, if a function returns a collection/map type then "no items" should translate to empty instance rather than null. When I say early exit I mean an initial condition is checked and it find the method has no work to do so I just want to return nothing/empty. – Mike Q Jul 26 '11 at 07:15