Reputation: 20993
I currently have an IEnumerable<MyObject>
where MyObject
has the properties String Name
and long Value
.
If i was to have within the Enumerable, 10 instances of MyObject
, each with a different name and value, with the exception of one having the same name as the other.
Does .NET (or LINQ) have a built in method which will allow me to find the duplicate, and if possible, merge the Value
property so that there ends up being only 9 elements within the enumerable, each with a distinct Name
and the one that had a duplicate has the Value
that is equal to the sum of its self and the duplicate.
So far i have found that the only way to iterate over the entire IEnumerable
and look for the duplicates and generate a new IEnumerable
of unique items, but this seems untidy and slow.
Upvotes: 11
Views: 2803
Reputation: 7475
list.GroupBy(e => e.Name).Select(group => new MyObject
{
Name = group.Key,
Value = group.Sum(e => e.Value)
}
)
Update:
Another variant:
list.GroupBy(
e => e.Name,
e => e,
(name, group) => group.Aggregate((result, e) =>
{
result.Value += e.Value;
return result;
}
)
)
Upvotes: 8
Reputation: 236188
You can group items by name and project results to 'merged' objects:
objects.GroupBy(o => o.Name)
.Select(g => new MyObject { Name = g.Key, Value = g.Sum(o => o.Value) });
UPDATE: Another option, if new MyObject instantiation is undesired (e.g. you have many properties in this class, or you should preserver references) then you can use aggregation with first item in group as accumulator:
objects.GroupBy(o => o.Name)
.Select(g => g.Skip(1).Aggregate(
g.First(), (a, o) => { a.Value += o.Value; return a; }));
Upvotes: 17
Reputation: 1775
Implement interface IEquatable and use Ditinct method. As follow:
internal class Program
{
private static void Main(string[] args)
{
var items = new List<MyClass>
{
new MyClass
{
Name = "Name1",
Value = 50
},
new MyClass
{
Name = "Name2",
Value = 20
},
new MyClass
{
Name = "Name3",
Value = 50
}
};
var distinct = items.Distinct().ToList();
}
}
internal class MyClass : **IEquatable<MyClass>**
{
public String Name { get; set; }
public int Value { get; set; }
**public bool Equals(MyClass other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return this.Value == other.Value;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return this.Equals((MyClass)obj);
}
public override int GetHashCode()
{
return this.Value;
}
public static bool operator ==(MyClass left, MyClass right)
{
return Equals(left, right);
}
public static bool operator !=(MyClass left, MyClass right)
{
return !Equals(left, right);
}**
}
Upvotes: 1
Reputation: 34238
I dont know a single method solution but what about:
set.GroupBy(g=>g.Name).Select(g=> new MyObject{Name=g.Key, Value=g.Sum(i=>i.Value)});
Upvotes: 3