如何基于属性合并两个列表?

时间:2011-10-11 19:19:08

标签: c# linq list

我有两个列表,一个是假的,一个是真的,比如:

BEFORE

// fake (list 1)
{ ID = 1, Year = 2011, X = "" }
, { ID = 2, Year = 2012, X = "" }
, { ID = 3, Year = 2013, X = "" }

// real (list 2)
{ ID = 35, Year = 2011, X = "Information" }
, { ID = 77, Year = 2013, X = "Important" }

我想合并他们寻找年份,结果应该是:

AFTER

{ ID = 35, Year = 2011, X = "Information" }
, { ID = 2, Year = 2012, X = "" }
, { ID = 77, Year = 2013, X = "Important" }

它必须在第一个列表中删除具有相同年份的元素,并将第二个列表中具有等效年份的元素添加到第一个列表中,保留顺序。

我怎样才能使用Linq?

4 个答案:

答案 0 :(得分:9)

您应该可以使用“左连接”来执行此操作:

from f in fake
join r in real
on f.Year equals r.Year
into joinResult
from r in joinResult.DefaultIfEmpty()
select new
       {
           ID = r == null ? f.ID : r.ID,
           Year = f.Year,
           X = r == null ? f.X : r.X
       };

答案 1 :(得分:5)

贾斯汀的查询是最有效的方法,但是如果你担心保留相同的对象(而不是从查询中创建新记录),你可以这样做:

var combined = from f in fake
               let r = (from r1 in real
                        where r1.Year == f.Year
                        select r1).SingleOrDefault()
               select r ?? f;

答案 2 :(得分:1)

不要自己定义假名单,而是让Linq为你做这件事:

Enumerable.Range(2011,3) //2011, 2012, 2013             
          //use the overload that provides a 0-based ordinal position of each element
          .Select(x,i=> new {ID = i+1, Year = x, X = String.Empty) 
          //now you have your fake list; join with the "real" list based on Year fields, 
          //taking the real element wherever it exists and the fake one otherwise
          .Join(real, l=>l.Year, r=>r.Year, (l,r) => r == null ? l : r);

这将产生您想要的结果集。但是,您可能需要为列表项定义命名类型,因为即使它们具有所有相同的成员类型/名称,也无法隐式转换两个单独定义的匿名类型。

答案 3 :(得分:1)

使用IEnumerable.UnionIEqualityComparer

P.S。如果真实列表具有更多元素(假列表中不存在的年数),则与左连接相比,这将导致不同的结果。左连接不会返回那些可能是期望结果的结果(OP中不清楚)。

public class MyClass
{
    public int ID {get; set;}
    public int Year {get; set;}
    public string X {get; set;}
}

public class MyClassEqualityComparer :  IEqualityComparer<MyClass>
{
    public bool Equals(MyClass x, MyClass y)
    {
        return x.Year == y.Year;
    }

    public int GetHashCode(MyClass obj)
    {
        return obj.ToString().ToLower().GetHashCode();
    }
}

void Main()
{
    var fake = new List<MyClass> {
          new MyClass { ID = 1, Year = 2011, X = "" }
        , new MyClass { ID = 2, Year = 2012, X = "" }
        , new MyClass { ID = 3, Year = 2013, X = "" }
    };

    var real = new List<MyClass> {
          new MyClass { ID = 35, Year = 2011, X = "Information" }
        , new MyClass { ID = 77, Year = 2013, X = "Important" }
    };

    var merged = real.Union(fake, new MyClassEqualityComparer());
}