0

Summary: Remove duplicates from custom List of Lists

I tried using various existing methods to remove the duplicates from other stack overflow posts, some examples would be:

var distinctWords = combinedList.Distinct().ToList();

fooArray.GroupBy(x => x.Id).Select(x => x.First());

These solutions only filter based on 1 criteria, I need to remove the duplicates based on 2 criteria, if the year and id match then the duplicate needs to be removed. There can be different years that have the same id. I would like to do this without using MoreLINQ.

Model:

    public class data
    {
        public List<Inventory> inventory { get; set; }
        public List<LocationInventory> locationinventory { get; set; }

    }

    public class Inventory
    {
        public string name{ get; set; }
        public string year{ get; set; }
        public string id{ get; set; }
    }

    public class LocationInventory
    {
        public string location { get; set; }
        public List<Inventory> inventory { get; set; }
    }

Controller:

        public ActionResult getData(string[] Make, string[] Location)
        {
            data inv = new Models.data();

            //Make a list of the inventory lists
            inv.locationinventory = new List<LocationInventory>();

            //Split the Manufacturer and Location at the commas and store in new array
            string[] Make_Values = Make[0].Split(',');
            string[] Location_Values = Location[0].Split(',');

            //Get values for the tables
            var popData = new List<Inventory>();

            foreach (var Location_Value in Location_Values)
            {
                //Populate the data for each location
                //populateData returns a List<Inventory>
                popData = populateData(Make_Values, Location_Value.ToString());

                //Add the data to model with cooresponding location and inventory
                inv.locationinventory.Add(new LocationInventory{ location = Location_Value, inventory = popData });
            }

            return PartialView("Table", inv);
        }

View:

@{
    var inventories = Model.locationinventory.Select(x => x.inventory).Distinct().ToList();
    var list1 = new List<Project1.Models.Inventory>();
    var list2 = new List<Project1.Models.Inventory>();

    foreach (var inv in inventories)
    {
        if (list1?.Any() != true)
        {
            list1 = inv;
        }

        if (list1?.Any() != false)
        {
            list2 = inv;
        }

        var combinedList = list1.Union(list2);

        var test = combinedList.GroupBy(x => new { x.id, x.year }).ToList();

        <div>
            <table>                
                @foreach (var l in test)
                {
                    foreach (var m in l)
                    {
                        <thead>
                            <tr>
                                <td>@m.name</td>
                                <td>@m.year</td>
                                <td>@m.id</td>
                            </tr>
                        </thead>
                    }
                }
            </table>
        </div>
    }

}

Current Results:

iphone 2019 x1234
iphone 2019 x1234
iphone 2020 x1234
iphone 2018 x1234
iwatch 2019 a4321
iwatch 2020 a4321
android 2020 n9876
android 2020 n9876
android 2019 n9876

Expected Results:

iphone 2019 x1234
iphone 2020 x1234
iphone 2018 x1234
iwatch 2019 a4321
iwatch 2020 a4321
android 2020 n9876
android 2019 n9876
  • If I understand you correctly, you need to implement a custom `IEqualityComparer` to perform the comparation based on your criteria. Take a look at the [official documentation here](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinct?view=netframework-4.8) – pappbence96 Oct 03 '19 at 13:15

1 Answers1

3

You are somewhat near to what you need but not complete. After grouping you need to project only one row from each group like:

var uniqueInventory = combinedList.GroupBy(x => new { x.name,x.id, x.year })
                                  .Select(x => x.First())
                                  .ToList();

Now it will contain one item for each group and all will be unique entries.

Another way is to implement IEqualityComparer which can be passed in the Distinct method.

public class InventoryComparer: IEqualityComparer<Inventory>
{
    public override bool Equals(Inventory a, Inventory b)
    {
        return a.name == b.name
            && a.id == b.id
            && a.year == b.year;
    }

    public override int GetHashCode(Inventory inventory)
    {
        return inventory.id.GetHashCode();
    }
}

and now we can use it like:

var uniqueInventory = combinedList.Distinct(new InventoryComparer());
Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
  • THANK YOU SO MUCH!!! This is exactly what I was trying to do, your solution worked perfectly! – DotNetFullStack Oct 03 '19 at 13:21
  • That is pretty cool, I will go ahead and try the second method as well to learn some more. – DotNetFullStack Oct 03 '19 at 13:24
  • @DotNetFullStack For completeness, also have a look at the [`DistinctBy()`](https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/DistinctBy.cs) method mentioned in the duplicate question's answers. – Matthew Watson Oct 03 '19 at 13:33