繁体   English   中英

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

[英]Add values of items within two IEnumerable collections

我有两个IEnumerable通用对象集合。 每个对象都有一个名称和值属性。 名称是字符串,值是双精度型。

我想将两者合并为一个并添加value属性。 例如,假设存在于两个集合中的对象具有以下属性:

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;
   }
}

可以使用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) });

为了使事情更快,您可以将第一个列表变成字典。 因此,您只需要迭代一次。 然后,您可以遍历第二遍并更新字典中的值,这非常快。

我不确定您是指“泛型类型的 集合 还是“匿名类型的集合” 匿名类型需要在我的解决方案中进行一些更改:

    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

如果您在同一列表中没有重复的键,则有一个左连接,但是解决方案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 });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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