Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to GroupBy a set of values using linq?

I know how it's possible to use anonymous types to group by a fixed list of values. What I want to do is group by an actual set of values.

For example, the result of this expression is 2.

new List<HashSet<int>> {
    new HashSet<int> { 4 },
    new HashSet<int> { 4 }
}.GroupBy (x => x).Count()

I'm looking for a way to put those sets in the same group so that the result would be 1. In python, this would be accomplished using frozenset.

What's the cleanest way of doing this?

like image 876
recursive Avatar asked Aug 02 '12 15:08

recursive


2 Answers

You can use the static HashSet<T>.CreateSetComparer method for this purpose.

Return Value

Type: System.Collections.Generic.IEqualityComparer> An IEqualityComparer object that can be used for deep equality testing of the HashSet object.

new List<HashSet<int>> {
    new HashSet<int> { 4 },
    new HashSet<int> { 4 }
}.GroupBy (x => x, HashSet<int>.CreateSetComparer())
like image 167
Ani Avatar answered Nov 08 '22 16:11

Ani


(I am assuming that you want to group both sets as "equal" -- the question is not terribly clear)

As is often the case with LINQ, the scaffolding to achieve this already exists and what needs to be done is to supply a custom IEqualityComparer<T> to the appropriate method. In this instance this means using this overload.

Here's a generic IEqualityComparer<ISet<T>> that declares two sets equal if their intersection is the same set as both of them:

class SetComparer<T> : IEqualityComparer<ISet<T>> {
    public bool Equals(ISet<T> lhs, ISet<T> rhs) {
        // null checks omitted
        return lhs.SetEquals(rhs);
    }

    public int GetHashCode(ISet<T> set) {
        // Not the best choice for a hash function in general,
        // but in this case it's just fine.
        return set.Count;
    }
}

And here's how you would group both sets under the same umbrella:

new List<HashSet<int>> {
    new HashSet<int> { 4 },
    new HashSet<int> { 4 }
}.GroupBy (x => x, new SetComparer<int>()).Count();
like image 31
Jon Avatar answered Nov 08 '22 14:11

Jon