2009-12-18 119 views
1

我有2个列表,我需要合并来自A和B的连接值,但也包括来自A和B的与连接不匹配的值。Linq查询合并2个列表

class TypeA 
{ 
    public string Key { get; set; } 
    public int ValueA { get; set; } 
} 

class TypeB 
{ 
    public string Key { get; set; } 
    public int ValueB { get; set; } 
} 

class TypeAB 
{ 
    public string Key { get; set; } 
    public int ValueA { get; set; } 
    public int ValueB { get; set; } 
} 

var listA = new List<TypeA> 
{ 
    new TypeA { Key = "one", Value = 1 }, 
    new TypeA { Key = "two", Value = 2 }, 
}; 

var listB = new List<TypeB> 
{ 
    new TypeB { Key = "two", Value = 2 }, 
    new TypeB { Key = "three", Value = 3 }, 
}; 

我想这些列表合并等于这个:

var listAB = new List<TypeAB> 
{ 
    new TypeAB { Key = "one", ValueA = 1, ValueB = null }, 
    new TypeAB { Key = "two", ValueA = 2, ValueB = 2 }, 
    new TypeAB { Key = "three", ValueA = null, ValueB = 3 }, 
}; 

什么是Linq的语句,将做到这一点?我一直在玩,不能完全到达那里。我可以通过在A到B和Union上进行左外连接来获得差不多的空间,但是我会得到重复的相交值。

更新

这里是我根据乔治的回答是什么:

var joined = 
    (from a in listA 
     join b in listB 
     on a.Key equals b.Key 
     into listBJoin 
     from b in listBJoin.DefaultIfEmpty(new TypeB()) 
     select new TypeAB 
     { 
     Key = a.Key, 
     ValueA = a.ValueA, 
     ValueB = b.ValueB, 
     }).Union(
     from b in listB 
     where !listA.Any(d => d.Key == b.Key) 
     select new TypeAB 
     { 
      Key = b.Key, 
      ValueB = b.ValueB, 
     } 
     ).ToList(); 
+0

发布另一个略微更有趣的响应 – 2009-12-18 20:27:40

+0

一些问题/答案,只是没有得到他们应得的upvotes。这两个答案都应该更好。他们至少得到了我的赞扬。 – 2010-03-17 17:49:37

回答

1

编辑:搞掂。这是马虎,但它应该工作。

listA //Start with lisA 
.Where(a=>!listB.Any(b=>a.Key == b.Key)) // Remove any that are duplicated in listB 
.Select(a => new TypeAB() { Key=a.Key, ValueA=a.Value}) // Map each A to an AB 
.Union(
    listB.Select(b => { 
     var correspondingA = listA.FirstOrDefault(a => a.Key == b.Key); //Is there an a that corresponds? 
    return new TypeAB() { Key=b.Key, 
     ValueB=b.Value, //Value B is easy 
     ValueA= correspondingA!=null ? (int?)correspondingA.Value : null //If there is an A than map its value 
    }; 
    }) 
) 

顺便说一句,如果你使用这个作为某种域操作,类型A和类型B的可能应该基于某种AorBIsAConceptThatHasMeaningInTheDomain基类。只要您发现自己将列表结合起来,这只是一个通用规则。如果不存在这样的概念,那么你可能不需要组合这些列表。另一方面,如果您将这作为映射的一部分(例如将域对象映射到UI),则可以使用匿名类型而不是TypeAB类来简化代码。 (或许不是,这只能靠个人喜好)

编辑编辑 下面是使用哈希

 var listAB = listA.Cast<object>().Union(listB.Cast<object>()).ToLookup(x => x is TypeA ? (x as TypeA).Key : (x as TypeB).Key) 
       .Select(kv => { 
        var a = kv.FirstOrDefault(x => x is TypeA) as TypeA; 
        var b = kv.FirstOrDefault(x => x is TypeB) as TypeB; 
        return new TypeAB() { 
         Key = kv.Key, 
         ValueA = a != null ? (int?)a.Value : null, 
         ValueB = b != null ? (int?)b.Value : null 
        }; 
       }).ToList(); 
+0

这将产生不正确的结果,例如键==“two”。实际上,它会有ValueA == null。 – 2009-12-18 19:51:27

+0

好点,错过了Req,嗯,如果在listA中有多个TypeA具有相同的键,会发生什么? – 2009-12-18 19:58:01

+0

你的回答给了我一个想法,在从listA到listB的LEFT OUTER JOIN之后不包括已经包含在listB中的结果。 – 2009-12-18 20:05:01

3

正是这种情况下,在我们的项目中,我们使用延长稍微理智有趣的答案称为Merge的方法。

public static class Extensions 
{ 
    public static IEnumerable<TResult> Merge<TLeft, TRight, TKey, TResult>(
     this IEnumerable<TLeft> leftList, 
     IEnumerable<TRight> rightList, 
     Func<TLeft, TKey> leftKeySelector, 
     Func<TRight, TKey> rightKeySelector, 
     Func<TKey, IEnumerable<TLeft>, IEnumerable<TRight>, TResult> combiner) 
    { 
     var leftLookup = leftList.ToLookup(leftKeySelector); 
     var rightLookup = rightList.ToLookup(rightKeySelector); 

     var keys = leftLookup.Select(g => g.Key).Concat(rightLookup.Select(g => g.Key)).Distinct(); 
     return keys.Select(key => combiner(key, leftLookup[key], rightLookup[key]));   
    } 
} 

您可以使用合并这样

var listAB = listA.Merge(
    listB, 
    a => a.Key, 
    b => b.Key, 
    (key, aItems, bItems) => new TypeAB 
    { 
     Key = key, 
     ValueA = aItems.Select(a => (int?)a.Value).SingleOrDefault(), 
     ValueB = bItems.Select(b => (int?)b.Value).SingleOrDefault() 
    }); 
+0

看起来很熟悉;-) – jeroenh 2009-12-19 21:10:36