我有两个IEnumerable通用对象集合。每个对象都有一个名称和值属性。名称是一个字符串,值为double。
我想将两者合并为一个并添加值属性。例如,假设两个集合中都存在具有以下属性的对象:
Collection 1: Collection 2:
Name: Var1 Name: Var1
Value: 2.67 Value: 4.32
在最后的收藏中,我想将上述两个合并为:
Result Collection:
Name: Var1
Value: 6.99
我知道使用foreach循环执行它的艰难/漫长的方式,通过迭代每个非常昂贵的东西,因为我的集合每个大约有150,000个对象。但是,我试图弄清楚使用Linq是否有更简单,更快捷的方法吗?
更新以回应Yuval Itzchakov的评论:
我尝试的漫长/艰难的方式是:
foreach(var item in collection1)
{
if(collection2.where(x => x.Name == item.Name).Count() == 1)
{
item.value += collection2.First(x => x.Name == item.Name).value;
}
}
答案 0 :(得分:5)
包含重复密钥的案例的最通用解决方案可以使用GroupBy和Sum LINQ方法实现:
var result = firstCollection
.Concat(secondCollection)
.GroupBy(x => x.Name)
.Select(g => new Foo { Name = g.Key, Value = g.Sum(f => f.Value) });
// Foo is an example of your element class. You can use anonymous classes
//.Select(g => new { Name = g.Key, Value = g.Sum(f => f.Value) });
此解决方案的算法复杂度为O(n)。
如果您希望获得最佳性能,可以使用序数字符串比较:
var result = firstCollection
.Concat(secondCollection)
.GroupBy(x => x.Name, x => x, StringComparer.Ordinal)
.Select(g => new Foo { Name = g.Key, Value = g.Sum(f => f.Value) });
答案 1 :(得分:3)
为了加快速度,您可以将第一个列表转换为字典。所以你只迭代一次。然后你可以遍历第二个并更新字典中的值,这非常快。
我不确定你是否意味着"泛型类型的集合" 或"匿名类型的集合" 。匿名类型需要在我的解决方案中进行一些更改:
public class Poco
{
public string Name { get; set; }
public double D { get; set; }
}
private static IEnumerable<Poco> Merge(IEnumerable<Poco> list1, IEnumerable<Poco> list2)
{
Dictionary<string, Poco> dict1 = list1.ToDictionary(l => l.Name, l => l);
foreach (Poco p in list2)
{
if (dict1.ContainsKey(p.Name))
{
Poco result = dict1[p.Name];
result.D += p.D;
yield return result;
continue;
}
yield return p;
}
}
注意:在此代码中我更改了源集合中的原始数据。因此,您可能需要创建Poco
的新实例以避免这种情况。只有当Name
每个集合都是唯一的时,它才会起作用。
编辑:第二个集合中多个Name
出现的处理方式与我在回答后添加的代码段略有不同。但我认为你可以做些改变。此代码中的性能要点是使用字典。
修改错过了continue
。
答案 2 :(得分:0)
如果您在同一列表中没有重复的密钥,则会有一个左连接 但解决方案https://stackoverflow.com/a/34439894/815590要好得多。
public class Poco
{
public string Name { get; set; }
public double Value { get; set; }
}
var listA = new List<Poco> { new Poco { Name = "Var1", Value = 2.67 } };
var listB = new List<Poco> { new Poco { Name = "Var1", Value = 4.32 } };
var merged = (from a in listA
join b in listB on a.Name equals b.Name into tempGroup
from a2 in tempGroup.DefaultIfEmpty()
select a2 == null ? a : new Poco { Name = a.Name, Value = a.Value + a2.Value });