John L.
John L.

Reputation: 1953

LINQ Grouping Based On Complex Condition

 public class SpecialClass
{
    public int property1;
    public ListAttributes attributes = new ListAttributes();
}


 public class ListAttributes
    {
        public List<a> aList = new List<a>();
        public List<b> bList = new List<b>();
        public List<c> cList = new List<c>();
        public List<d> dList = new List<d>();
    }

I have a List of SpecialClass objects. I want to group them such that each SpecialClass object having the same aList, bList, cList and dList will be in the same group.

Condition for these lists to be equal can be expressed as:

var areEquivalent = !list1.Except(list2).Union(list2.Except(list1)).Any();

In other words, these distinct lists should have the same members regardless of their place in the list.

How can this be done using LINQ? (I would appreciate it if it can be expressed as lambda expressions but query expressions form is also welcome)

Upvotes: 1

Views: 66

Answers (1)

mjwills
mjwills

Reputation: 23975

One option would be to write a custom EqualityComparer. This sample might get you started.

using System;
using System.Collections.Generic;
using System.Linq;

public class ListAttributesEqualityComparer : IEqualityComparer<ListAttributes>
{
    public bool Equals(ListAttributes x, ListAttributes y)
    {
        if (ReferenceEquals(x, y))
            return true;

        if (x == null || y == null)
            return false;

        // You should also do null checks on aList etc, but I have left those out for brevity
        var result = x.aList.Count == y.aList.Count && x.bList.Count == y.bList.Count && x.cList.Count == y.cList.Count &&
                     x.dList.Count == y.dList.Count;
        result = result && !x.aList.Except(y.aList).Union(y.aList.Except(x.aList)).Any();
        result = result && !x.bList.Except(y.bList).Union(y.bList.Except(x.bList)).Any();
        result = result && !x.cList.Except(y.cList).Union(y.cList.Except(x.cList)).Any();
        result = result && !x.dList.Except(y.dList).Union(y.dList.Except(x.dList)).Any();
        return result;
    }

    public int GetHashCode(ListAttributes obj)
    {
        return (obj?.aList?.Distinct()?.Count() ?? 0).GetHashCode();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var list = new List<SpecialClass>();

        // Populate list here

        var results = list.GroupBy(g => g.attributes, new ListAttributesEqualityComparer());

        Console.ReadLine();
    }
}

public class SpecialClass
{
    public int property1;
    public ListAttributes attributes = new ListAttributes();
}

public class ListAttributes
{
    public List<int> aList = new List<int>();
    public List<string> bList = new List<string>();
    public List<double> cList = new List<double>();
    public List<DateTime> dList = new List<DateTime>();
}

Also consider simplifying your existing comparison to use Intersect like mentioned in the comments of your original question.

Upvotes: 2

Related Questions