Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ for comparing two lists with complex entities

Tags:

c#

linq

We have a class structure as listed below

public class ItemDTO
{
    public int ItemID { get; set; }
}


public class CostPageDTO
{
    public string CostPageNumber { get; set; }
    public List<ItemDTO> Items { get; set; }
}

There are two lists – 1) List of Costpage and Items present in the database 2) User selected costpages

QUESTION

We need to compare these two lists and get a resultant list that has costpages for which the count of distinct items in the actual set and selected set are same.

What is the best performing LINQ for this (in Chain-Method approach)?

EXPECTED RESULT

Expected Result based on the following scenario is a list with only 1 costpage – “C2” (for which items match)

enter image description here

CODE

static void Main(string[] args)
{

    List<CostPageDTO> selectedCostPageAndItems = GetSelectedCostPageAndItems();
    List<CostPageDTO> actualItems = GetActualItems();

    //LINQ code to get the matching count costPages

}


private static List<CostPageDTO> GetSelectedCostPageAndItems()
{
    ItemDTO i1 = new ItemDTO();
    i1.ItemID = 1;

    ItemDTO i2 = new ItemDTO();
    i2.ItemID = 2;

    ItemDTO i3 = new ItemDTO();
    i3.ItemID = 3;


    CostPageDTO c1 = new CostPageDTO();
    c1.CostPageNumber = "C1";
    c1.Items = new List<ItemDTO>();
    c1.Items.Add(i1);

    CostPageDTO c2 = new CostPageDTO();
    c2.CostPageNumber = "C2";
    c2.Items = new List<ItemDTO>();
    c2.Items.Add(i2);
    c2.Items.Add(i3);

    //CostPageDTO c2Duplicate = new CostPageDTO();
    //c2Duplicate.CostPageNumber = "C2";
    //c2Duplicate.Items = new List<ItemDTO>();
    //c2Duplicate.Items.Add(i2);
    //c2Duplicate.Items.Add(i3);

    List<CostPageDTO> selectedCostPageAndItems = new List<CostPageDTO>();
    selectedCostPageAndItems.Add(c1);
    selectedCostPageAndItems.Add(c2);
    //selectedCostPageAndItems.Add(c2Duplicate);

    return selectedCostPageAndItems;

}
private static List<CostPageDTO> GetActualItems()
{
    ItemDTO i1 = new ItemDTO();
    i1.ItemID = 1;

    ItemDTO i2 = new ItemDTO();
    i2.ItemID = 2;

    ItemDTO i3 = new ItemDTO();
    i3.ItemID = 3;

    ItemDTO i3Duplicate = new ItemDTO();
    i3Duplicate.ItemID = 3;

    CostPageDTO c1 = new CostPageDTO();
    c1.CostPageNumber = "C1";
    c1.Items = new List<ItemDTO>();
    c1.Items.Add(i1);
    c1.Items.Add(i2);
    c1.Items.Add(i3);

    CostPageDTO c2 = new CostPageDTO();
    c2.CostPageNumber = "C2";
    c2.Items = new List<ItemDTO>();
    c2.Items.Add(i2);
    c2.Items.Add(i3);
    c2.Items.Add(i3Duplicate);

    List<CostPageDTO> actualItems = new List<CostPageDTO>();
    actualItems.Add(c1);
    actualItems.Add(c2);

    return actualItems;

}
like image 806
LCJ Avatar asked Oct 02 '22 03:10

LCJ


1 Answers

This will select your expected result:

Method syntax:

var result = selectedCostPageAndItems.SelectMany(scp => actualItems.Where(acp=>acp.CostPageNumber == scp.CostPageNumber),
                            (scp,acp)=> new {CostPageNumber = scp.CostPageNumber,
                                        scpItems = new HashSet<int>(scp.Items.Select(x=>x.ItemID)),
                                        acpItems = new HashSet<int>(acp.Items.Select(x=>x.ItemID))})
                        .Where(x => x.scpItems.SetEquals(x.acpItems))
                        .Select(x => x.CostPageNumber);

Query comprehension syntax:

var result = from cp1 in selectedCostPageAndItems
             from cp2 in actualItems
             let items1 = new HashSet<int>(cp1.Items.Select(x=>x.ItemID))
             let items2 = new HashSet<int>(cp2.Items.Select(x=>x.ItemID))
             where cp1.CostPageNumber == cp2.CostPageNumber
             && items1.SetEquals(items2)
             select cp1.CostPageNumber;

Explanation:

Find all pairs of selected (scp) and actual (acp) CostPageDTO with identical CostPageNumber

selectedCostPageAndItems.SelectMany(scp => actualItems.Where(acp=>acp.CostPageNumber == scp.CostPageNumber)

For each such pair (scp and acp), select the CostPageNumber and a set of ItemDTO.ItemID for the selected (scpItems) and actual (acpItems) CostPageDTO

(scp,acp)=> new {CostPageNumber = scp.CostPageNumber,
                                    scpItems = new HashSet<int>(scp.Items.Select(x=>x.ItemID)),
                                    acpItems = new HashSet<int>(acp.Items.Select(x=>x.ItemID))})

Finally select the CostPageNumber of the ones that have equal sets

Where(x => x.scpItems.SetEquals(x.acpItems)).Select(x => x.CostPageNumber)

The creation and the comparison of the HashSet is all O(n)

like image 182
Johnbot Avatar answered Oct 13 '22 10:10

Johnbot