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.