Reputation: 3028
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;
Upvotes: 229
Views: 126502
Reputation: 1499760
Edit (2023): There is now an ToHashSet
extension method - see Douglas' answer below.
Original Answer:
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.
Upvotes: 333
Reputation: 54877
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.
ToHashSet<TSource>(IEnumerable<TSource>)
ToHashSet<TSource>(IEnumerable<TSource>, IEqualityComparer<TSource>)
Upvotes: 54
Reputation: 3028
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());
}
Upvotes: 2
Reputation: 13559
There is an extension method build in the .NET framework and in .NET core for converting an IEnumerable
to a HashSet
: https://learn.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);
Upvotes: 4
Reputation: 7805
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.
Upvotes: 3
Reputation: 4102
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);
}
Upvotes: 1
Reputation: 21742
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 :)
Upvotes: 3
Reputation: 61579
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);
}
Upvotes: 21
Reputation: 28735
Just pass your IEnumerable into the constructor for HashSet.
HashSet<T> foo = new HashSet<T>(from x in bar.Items select x);
Upvotes: 90