简体   繁体   中英

Remove from List<> duplicated items and record duplicated quantity

I have a List<Item> . Item has properties Id , Name and Amount . There are duplicated items in this list. I need to get a new List<Item> which contains only non-duplicated Item s and in Item 's Amount should be the quantity of how many times it duplicated in first List<Item> . I tried something like

            for (int i = 0; i < list.Count; i++)
            {
                for (int j = 0; j < list.Count; j++)
                {
                        if (list[i].Name == list[j].Name)
                        {
                            list.Remove(prod.Components[j]);
                            list[i].Amount++;
                        }
                }
            }

but there are some problems in this loop. My brains are overheated. Please, help.

A simple LINQ query can get you the unique items along with the number of times they appear:

var distinct = list.GroupBy(o => o.Name)
                   .Select(g => new { Count = g.Count(), Item = g.First() })
                   .ToList();

Then you can modify each item's Amount to the count of duplicates:

foreach (var row in distinct)
{
    row.Item.Amount = row.Count;
}

And finally get back a List<Item> that contains no duplicates and has the correct amounts:

var uniqueItems = distinct.Select(r => r.Item).ToList();

Important: The code above assumes that "duplicate" items are indistinguishable from each other, but nothing else (eg it doesn't need Item to have a default constructor). Depending on the particulars it may be possible to write it in an even shorter form.

Additionally, the Amount property looks strange here. Since duplicates do not warrant summation of their amounts, what's the purpose of Item.Amount ? I would assume that duplicate items with amount of 2 should result in one item with an amount of 4, but your code does not do that (and mine follows that lead).

Off the top of my head (haven't tested it):

list.GroupBy(x => x.Name)
    .Select(x => new Item {
                            Name = x.Key,
                            Amount = x.Count()
                          })
    .ToList();

You haven't specified what happens to the Ids, so I've left ignored them.

(Note this creates a new list, rather than modifying the original).

Try something like that:

var groups = from item in items
             group item by item.Property
             into grouped
             select grouped;

var distinct = from g in groups
               let item = g.First()
               let amount = g.Count()
               select new Item {Property = item.Property, Amount = amount};

After that distinct contains IEnumerable<Item> with their amount from original items list.

Assuming that you determine duplicates by the first two properties ID and Name .

You can implement an IEqualityComparer<Item> and use that for Enumerable.GroupBy :

var itemAmounts = items.GroupBy(i => i, new Item())
    .Select(g => new Item { 
        ID = g.First().ID, 
        Name = g.First().Name, 
        Amount = g.Count()
    });

Here's your Item class with a meaningful implementation of IEqualityComparer<Item> :

class Item : IEqualityComparer<Item>
{
    public int ID;
    public string Name;
    public int Amount;

    public bool Equals(Item x, Item y)
    {
        if (x == null || y == null) return false;

        bool equals = x.ID == y.ID && x.Name == y.Name;
        return equals;
    }

    public int GetHashCode(Item obj)
    {
        if (obj == null) return int.MinValue;

        int hash = 19;
        hash = hash + obj.ID.GetHashCode();
        hash = hash + obj.Name.GetHashCode();
        return hash;
    }
}

You could also override Equals and GetHasdhCode from object , then you don't need a custom comparer at all in GroupBy :

var itemAmounts = items.GroupBy(i => i)
    .Select(g => new Item { 
        ID = g.First().ID, 
        Name = g.First().Name, 
        Amount = g.Count()
    });

You can use above already available methods:

public override bool Equals(object obj)
{
    Item item2 = obj as Item;
    if (item2 == null)
        return false;
    else
        return Equals(this, item2);
}

public override int GetHashCode()
{
    return GetHashCode(this);
}
foreach(var item in list)
{
    if(list.Count(e=>e.Id == item.Id && e.Name == item.Name)!=1)
    {
        list.Remove(item);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM