Search code examples
c#linquniquelinq-group

Per Invoice show string/int array of unique products


I have a list of Invoices and all the Products on each Invoice. Each Invoice can have multiples of the same product

class InvoiceProducts 
{
    public int InvoiceID { get; set; }
    public int ProductID { get; set; }
}


var list = new List<InvoiceProducts>();
list.Add(new { InvoiceID = 7000, ProductID=15});
list.Add(new { InvoiceID = 7000, ProductID=10});
list.Add(new { InvoiceID = 7000, ProductID=10});
list.Add(new { InvoiceID = 7000, ProductID=15});

list.Add(new { InvoiceID = 7010, ProductID=12});
list.Add(new { InvoiceID = 7010, ProductID=20});
list.Add(new { InvoiceID = 7010, ProductID=12});

list.Add(new { InvoiceID = 7021, ProductID=1});
list.Add(new { InvoiceID = 7021, ProductID=1});

Can I please ask assistance in grouping per InvoiceID, and having a (Sorted) integer list of unique Products per invoice (The reason for sorting is that I need to match this with other invoices with the same products later)

i.e.

InvoiceID   ProductID
7000        10,15       
7010        12,20
7021        1

Failed attempts:

  var tl2 = List
      .GroupBy(x => x.InvoiceID)
      .ToDictionary(y => y.Key, y => y.Distinct().ToList());

Failed Attempt explained : it has a dictionary which grouped correctly by InvoiceID, but invoice 7000 had 4 line items instead of 2 unique products


Solution

  • You want ToLookup here - it's designed for precisely this scenario.

    var lookup = list.ToLookup(x => x.InvoiceID, x => x.ProductID);
    

    That will still contain the duplicate product IDs, but you can easily make them distinct when you fetch them:

    var products = list[7000].Distinct();
    

    Or you could just use Distinct() on your list:

    var lookup = list.Distinct()
                     .ToLookup(x => x.InvoiceID, x => x.ProductID);
    

    That would work with the code using the anonymous type, but not if you actually use your InvoiceProducts type. You could always project:

    var lookup = list.Select(x => new { x.InvoiceID, x.ProductID })
                     .Distinct()
                     .ToLookup(x => x.InvoiceID, x => x.ProductID);
    

    ... or just make your InvoiceProducts type implement equality appropriately.