Search code examples
c#linqiequalitycomparer

How to find overlapping in collection using LINQ ( not duplicates but to find overlaps)


List<StoreDetailDto> items = new List<StoreDetailDto>();

items.Add(new StoreDetailDto { StoreNumber = 2, WarehouseNumber = 4201, IsResolved = false, StoreName = "StoreEx", WarehouseName = "WarehouseEx1"});
items.Add(new StoreDetailDto { StoreNumber = 3, WarehouseNumber = 4201, IsResolved = false, StoreName = "StoreEx", WarehouseName = "WarehouseEx1"});
items.Add(new StoreDetailDto { StoreNumber = 6, WarehouseNumber = 4201, IsResolved = false, StoreName = "StoreEx", WarehouseName = "WarehouseEx1"});
items.Add(new StoreDetailDto { StoreNumber = 7, WarehouseNumber = 4201, IsResolved = false, StoreName = "StoreEx", WarehouseName = "WarehouseEx1"});
items.Add(new StoreDetailDto { StoreNumber = 9, WarehouseNumber = 4201, IsResolved = false, StoreName = "StoreEx", WarehouseName = "WarehouseEx1"});

items.Add(new StoreDetailDto { StoreNumber = 6, WarehouseNumber = 4202, IsResolved = false, StoreName = "StoreEx", WarehouseName = "WarehouseEx1"});
items.Add(new StoreDetailDto { StoreNumber = 7, WarehouseNumber = 4201, IsResolved = false, StoreName = "StoreEx", WarehouseName = "WarehouseEx1"});
items.Add(new StoreDetailDto { StoreNumber = 9, WarehouseNumber = 4203, IsResolved = false, StoreName = "StoreEx", WarehouseName = "WarehouseEx1"});
items.Add(new StoreDetailDto { StoreNumber = 9, WarehouseNumber = 4207, IsResolved = false, StoreName = "StoreEx", WarehouseName = "WarehouseEx1"});

I have collection above where there is a overlapping between store 6, 9 but store 7 is not overlap it's a duplicate. I am finding overlaps as such that same store with different warehouse.

in order to acheive that I am doing below:

var overlapDupStores = items.GroupBy(
    u => u.StoreNumber,
    u => { return u; },
    (key, g) => g.ToList())
  .ToList()
  .Where(cnt => cnt.Count() > 1);


foreach (var dpovl in overlapDupStores)
{
    var stores = dpovl.Where(g => g.IsResolved != true).GroupBy(u => new { u.StoreNumber, u.WarehouseNumber }).ToList();

    if (stores.Count() > 1)
    {
        response.OverlappingStores.AddRange(stores.SelectMany(gr => gr).ToList());
    }
}

I am first finding stores which are duplicates , it will have object inside object with duplicates of stores with count = 2 and then grouping by storenumber and warehouse number for different store and warehouse count will still be 2 but for same store and warehouse which is duplicate it will be count = 1 so i am finding count > 1 so i can find duplicates and check Isresolved not to pull same store again once it is resolved.

Below is the fiddle:

https://dotnetfiddle.net/TknnkJ


Solution

  • Try this one, using LINQ's GroupBy().Select().Where() to group and filter the list of objects based on the criteria that I assume you require, given the output you showed.

    A description:

    GroupBy() => Groups the objects using the StoreNumber property.
    Select() => Selects the objects where the IsResolved property is false
    Where() => Filters the objects that have more than 1 elements with different WarehouseNumber
    SelectMany()=> Flattens the Grouping, to have all the matching objects in a sequential view

    OrderBy() could be added, if required, to order the list on StoreNumber ThenBy() WarehouseNumber.

    The Duplicates filter may not be correct, depending on the use case. It might be necessary to adapt it.

    var Duplicates = 
        items.GroupBy(store => store.StoreNumber)
             .Select(grp => grp.Where(store => store.IsResolved == false))
             .Where(stores => stores.Count() > 1 && stores.Select(w => w.WarehouseNumber).Distinct().Count() == 1)
             .SelectMany(stores => stores)
             .ToList();
    
    var Overlapping = 
        items.GroupBy(store => store.StoreNumber)
             .Select(grp => grp.Where(store => store.IsResolved == false))
             .Where(store => store.Count() > 1 && store.Select(w => w.WarehouseNumber).Distinct().Count() > 1)
             .SelectMany(stores => stores)
             .ToList();
    
    Overlapping.ForEach(ovr =>
        Console.WriteLine($"{ovr.StoreNumber} " +
                          $"{ovr.WarehouseNumber} " +
                          $"{ovr.IsResolved} " +
                          $"{ovr.StoreName} " +
                          $"{ovr.WarehouseName}"));
    

    This Overlapping List prints:

    6 4201 False StoreEx WarehouseEx1
    6 4202 False StoreEx WarehouseEx1
    9 4201 False StoreEx WarehouseEx1
    9 4203 False StoreEx WarehouseEx1
    9 4207 False StoreEx WarehouseEx1
    

    This Duplicates List prints:

    7 4201 False StoreEx WarehouseEx1
    7 4201 False StoreEx WarehouseEx1