Q:

Linq Group by Complex Type of List

I'm trying to do a Linq GroupBy on some list objects of a list.
public class ItemDto
{

    public int ItemId { get; set; }
    public string ProductName { get; set; }
    public int Count { get; set; }
    public List<CondimentDto> Condiments { get; set; }
}

public class CondimentDto
{
    public CondimentDto(int ItemId, string productName, decimal originalPrice)
    {
        ItemId = ItemId;
        ProductName = productName;
        OriginalPrice = originalPrice;

    }
    public int PosItemId { get; set; }
    public string ProductName { get; set; }
    public decimal OriginalPrice { get; set; }

}

Here is list example.
var items = new List<ItemDto>();

items.Add(new ItemDto(){ItemId = 1, ProductName = "Steak", Condiments = new List<CondimentDto>{new CondimentDto(11, "Mayonez", 1)}});
items.Add(new ItemDto() { ItemId = 1, ProductName = "Steak", Condiments = new List<CondimentDto> { new CondimentDto(11, "Mayonez", 1) } });
items.Add(new ItemDto() { ItemId = 1, ProductName = "Steak", Condiments = new List<CondimentDto> { new CondimentDto(11, "Hardal", 1) } });

Here is my LinQ Query;
var result = items.GroupBy(item => new {item.ItemId, item.Condiments})
                  .Select(x=>new ItemDto
                  {
                     ItemId = x.Key,
                     ProductName = x.First().ProductName,
                     Condiments = x.First().Condiments,
                     Count = x.Count()

                  })
                  .ToList();

What I want to do is I want to group my list by ItemId and List of Condiments. The result that I expect is
{
  items[
    {
      "ItemId": 1,
      "ProductName": "Steak",
      "Condiments": [
        {
          "ItemId": 11,
          "Name": "Mayonez",
          "Price": 1
        }
      ],
      "Count": 2
    },
    {
      "ItemId": 1,
      "ProductName": "Steak",
      "Condiments": [
        {
          "ItemId": 11,
          "Name": "Hardal",
          "Price": 1
        }
      ],
      "Count": 1
    }
  ]
}

As a result there are two same list item that's why it has to be grouped and count is 2. And one different item which is condiment is Hardal. It's separate and count is 1.
Is it possible two group by a list object? By the way condiments can be null. Then it should only group by Id. 
Thank you 

A:

You could create two IEqualityComparer<T> classes here. One for comparing if two CondimentDto objects are equal and one for comparing if two List<CondimentDto> are equal. We pass the CondimentComparer to SequenceEqual() to determine if two lists are equal, and we pass ItemDtoComparer to GroupBy to group ItemDto objects from the main items list. We have to also create our own Equals() and GetHashCode() to determine the equality. 
public class CondimentComparer : IEqualityComparer<CondimentDto>
{
    public bool Equals(CondimentDto x, CondimentDto y)
    {
        // If all the properties match, then its equal
        return x.PosItemId == y.PosItemId && 
               x.ProductName == y.ProductName && 
               x.OriginalPrice == y.OriginalPrice;
    }

    // Get all hashcodes from object properties using XOR
    // Might be better ways to do this
    public int GetHashCode(CondimentDto obj)
    {
        return obj.PosItemId.GetHashCode() ^ 
               obj.ProductName.GetHashCode() ^
               obj.OriginalPrice.GetHashCode();
    }
}

public class ItemDtoComparer : IEqualityComparer<ItemDto>
{
    public bool Equals(ItemDto x, ItemDto y)
    {

        // If two condiments are null, then just compare ItemId
        if (x.Condiments == null && y.Condiments == null)
        {
            return x.ItemId == y.ItemId;
        }

        // If one is null and the other is not, can't possiblly be equal
        else if (x.Condiments == null && y.Condiments != null ||
                 x.Condiments != null && y.Condiments == null)
        {
            return false;
        }

        // If we get here we have two normal objects
        // Compare two objects ItemId and lists of condiments
        return x.ItemId == y.ItemId &&
               x.Condiments.SequenceEqual(y.Condiments, new CondimentComparer());
    }

    public int GetHashCode(ItemDto obj)
    {

        // If condiments list is null, just get the ItemId hashcode
        if (obj.Condiments == null)
        {
            return obj.ItemId.GetHashCode();
        }

        // Otherwise do the XOR of ItemId and the aggregated XOR of the condiment list properties
        return obj.ItemId.GetHashCode() ^ obj.Condiments.Aggregate(0, (a, c) => a ^ c.PosItemId.GetHashCode() ^ c.ProductName.GetHashCode() ^ c.OriginalPrice.GetHashCode());
    }
}

Then you can pass ItemDtoComparer to Groupby:
var result = items
    .GroupBy(item => item, new ItemDtoComparer()) // Pass ItemDtoComparer here
    .Select(grp => new ItemDto
    {
        ItemId = grp.Key.ItemId,
        ProductName = grp.Key.ProductName,
        Condiments = grp.Key.Condiments,
        Count = grp.Count()
    })
    .ToList();

Demo on dotnetfiddle.net
Note: The above GetHashCode() implementations are XORing hashcodes. This is considered to not be best practice, and following the suggestions in this answer offer better alternatives to avoiding hash collisions. I just chose this method as its easy to remember and demo. 

