How to convert linq results to HashSet or HashedSet

C#.NetLinq

C# Problem Overview


I have a property on a class that is an ISet. I'm trying to get the results of a linq query into that property, but can't figure out how to do so.

Basically, looking for the last part of this:

ISet<T> foo = new HashedSet<T>();
foo = (from x in bar.Items select x).SOMETHING;

Could also do this:

HashSet<T> foo = new HashSet<T>();
foo = (from x in bar.Items select x).SOMETHING;

C# Solutions


Solution 1 - C#

I don't think there's anything built in which does this... but it's really easy to write an extension method:

public static class Extensions
{
    public static HashSet<T> ToHashSet<T>(
        this IEnumerable<T> source,
        IEqualityComparer<T> comparer = null)
    {
        return new HashSet<T>(source, comparer);
    }
}

Note that you really do want an extension method (or at least a generic method of some form) here, because you may not be able to express the type of T explicitly:

var query = from i in Enumerable.Range(0, 10)
            select new { i, j = i + 1 };
var resultSet = query.ToHashSet();

You can't do that with an explicit call to the HashSet<T> constructor. We're relying on type inference for generic methods to do it for us.

Now you could choose to name it ToSet and return ISet<T> - but I'd stick with ToHashSet and the concrete type. This is consistent with the standard LINQ operators (ToDictionary, ToList) and allows for future expansion (e.g. ToSortedSet). You may also want to provide an overload specifying the comparison to use.

Solution 2 - C#

Just pass your IEnumerable into the constructor for HashSet.

HashSet<T> foo = new HashSet<T>(from x in bar.Items select x);

Solution 3 - C#

This functionality has been added as an extension method on IEnumerable<TSource> to .NET Framework 4.7.2 and .NET Core 2.0. It is consequently also available on .NET 5 and later.

Solution 4 - C#

As @Joel stated, you can just pass your enumerable in. If you want to do an extension method, you can do:

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items)
{
    return new HashSet<T>(items);
}

Solution 5 - C#

There is an extension method build in the .NET framework and in .NET core for converting an IEnumerable to a HashSet: https://docs.microsoft.com/en-us/dotnet/api/?term=ToHashSet

public static System.Collections.Generic.HashSet<TSource> ToHashSet<TSource> (this System.Collections.Generic.IEnumerable<TSource> source);

It appears that I cannot use it in .NET standard libraries yet (at the time of writing). So then I use this extension method:

    [Obsolete("In the .NET framework and in NET core this method is available, " +
              "however can't use it in .NET standard yet. When it's added, please remove this method")]
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer = null) => new HashSet<T>(source, comparer);

Solution 6 - C#

If you need just readonly access to the set and the source is a parameter to your method, then I would go with

public static ISet<T> EnsureSet<T>(this IEnumerable<T> source)
{
    ISet<T> result = source as ISet<T>;
    if (result != null)
        return result;
    return new HashSet<T>(source);
}

The reason is, that the users may call your method with the ISet already so you do not need to create the copy.

Solution 7 - C#

That's pretty simple :)

var foo = new HashSet<T>(from x in bar.Items select x);

and yes T is the type specified by OP :)

Solution 8 - C#

Jon's answer is perfect. The only caveat is that, using NHibernate's HashedSet, I need to convert the results to a collection. Is there an optimal way to do this?

ISet<string> bla = new HashedSet<string>((from b in strings select b).ToArray()); 

or

ISet<string> bla = new HashedSet<string>((from b in strings select b).ToList()); 

Or am I missing something else?


Edit: This is what I ended up doing:

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
{
    return new HashSet<T>(source);
}

public static HashedSet<T> ToHashedSet<T>(this IEnumerable<T> source)
{
    return new HashedSet<T>(source.ToHashSet());
}

Solution 9 - C#

Rather than the simple conversion of IEnumerable to a HashSet, it is often convenient to convert a property of another object into a HashSet. You could write this as:

var set = myObject.Select(o => o.Name).ToHashSet();

but, my preference would be to use selectors:

var set = myObject.ToHashSet(o => o.Name);

They do the same thing, and the the second is obviously shorter, but I find the idiom fits my brains better (I think of it as being like ToDictionary).

Here's the extension method to use, with support for custom comparers as a bonus.

public static HashSet<TKey> ToHashSet<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> selector,
    IEqualityComparer<TKey> comparer = null)
{
    return new HashSet<TKey>(source.Select(selector), comparer);
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJamieView Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#Joel MuellerView Answer on Stackoverflow
Solution 3 - C#DouglasView Answer on Stackoverflow
Solution 4 - C#Matthew AbbottView Answer on Stackoverflow
Solution 5 - C#Nick N.View Answer on Stackoverflow
Solution 6 - C#xmedekoView Answer on Stackoverflow
Solution 7 - C#Rune FSView Answer on Stackoverflow
Solution 8 - C#JamieView Answer on Stackoverflow
Solution 9 - C#StephenDView Answer on Stackoverflow