在两个IEnumerable集合中添加项的值

时间:2015-12-23 16:09:17

标签: c# algorithm linq

我有两个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;
   }
}

3 个答案:

答案 0 :(得分:5)

包含重复密钥的案例的最通用解决方案可以使用GroupBySum 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 });