与Linq叠加/加入两个集合

问题描述:

我有以下情形:与Linq叠加/加入两个集合

列表1有20个TItem类型的项目,列表2有5个相同类型的项目。清单1已经包含清单2中的项目,但处于不同的状态。我想用列表2中的项目覆盖列表1中的5个项目。

我认为一个联接可能工作,但我想覆盖列表1中的项目,而不是将它们连接在一起并具有重复项目。

有是可用于发现在列表1覆盖哪些项目关键是int类型的唯一密钥

你可以使用内置的LINQ .Except()但它想要一个IEqualityComparer,因此使用.Except()的a fluid version来代替。

假设与的整数键的对象作为你指示:

public class Item 
{ 
    public int Key { get; set; } 
    public int Value { get; set; } 
    public override string ToString() 
    { 
     return String.Format("{{{0}:{1}}}", Key, Value); 
    } 
} 

对象的原始列表可以与被改变的一个如下被合并:

IEnumerable<Item> original = new[] { 1, 2, 3, 4, 5 }.Select(x => new Item 
    { 
     Key = x, 
     Value = x 
    }); 
IEnumerable<Item> changed = new[] { 2, 3, 5 }.Select(x => new Item 
    { 
     Key = x, 
     Value = x * x 
    }); 

IEnumerable<Item> result = original.Except(changed, x => x.Key).Concat(changed); 
result.ForEach(Console.WriteLine); 

输出:

{1:1} 
{4:4} 
{2:4} 
{3:9} 
{5:25} 

LINQ不用于进行实际的修改底层数据源;这完全是一种查询语言。当然,你可以在List2上从List1做一个外连接,如果它不是null,那么选择List2的实体,如果它是的实体,但是这会给你一个IEnumerable<>的结果;它不会真正修改集合。您可以对结果执行ToList()并将其分配给List1,但这会改变引用;我不知道这是否会影响您的其他应用程序。

考虑您的问题从字面上看,在你想如果存在的话,以取代List1与那些从List2的项目,那么你就必须是手工做的for遍历List1,检查相应的存在输入List2,并用List2的索引替换List1

正如Adam所说,LINQ是关于查询的。但是,您可以使用Enumerable.Union以正确的方式创建新的集合。您需要创建一个合适的IEqualityComparer - 如果拥有UnionBy将会很愉快。 (另外一个为MoreLINQ也许?)

基本上是:

var list3 = list2.Union(list1, keyComparer); 

keyComparer将是一个实现这两个键进行比较。 MiscUtil包含一个ProjectionEqualityComparer这将使这个稍微容易一些。

或者,您可以连接后使用DistinctBy从MoreLINQ:

var list3 = list2.Concat(list1).DistinctBy(item => item.Key); 

下面是与群组加入一个解决方案。

 List<string> source = new List<string>() { "1", "22", "333" }; 
     List<string> modifications = new List<string>() { "4", "555"}; 
      //alternate implementation 
     //List<string> result = source.GroupJoin(
     // modifications, 
     // s => s.Length, 
     // m => m.Length, 
     // (s, g) => g.Any() ? g.First() : s 
     //).ToList(); 

     List<string> result = 
     (
      from s in source 
      join m in modifications 
      on s.Length equals m.Length into g 
      select g.Any() ? g.First() : s 
     ).ToList(); 

     foreach (string s in result) 
      Console.WriteLine(s); 

嗯,怎么样一个可重复使用的扩展方法,而我在这:

public static IEnumerable<T> UnionBy<T, U> 
    (
     this IEnumerable<T> source, 
     IEnumerable<T> otherSource, 
     Func<T, U> selector 
    ) 
    { 
     return source.GroupJoin(
      otherSource, 
      selector, 
      selector, 
      (s, g) => g.Any() ? g.First() : s 
       ); 
    } 

这是由叫做:

List<string> result = source 
    .UnionBy(modifications, s => s.Length) 
    .ToList();