1

我有一个类项目

public class Project 
{   public int ProjectId { get; set; }
    public string ProjectName { get; set; }
    public string Customer { get; set; }
    public string Address{ get; set; }
}

我有 3 个列表

List<Project> lst1; List<Project> lst2; List<Project> lst3;

lst1包含Person具有 ProjectId 和 ProjectName 的对象。

ProjectId =1, ProjectName = "X", Customer = null, Address = null

ProjectId =2, ProjectName = "Y", Customer = null, Address = null

lst2包含Person具有 ProjectId 和 Customer 的对象

ProjectId =1,ProjectName = null, Customer = "c1", Address = null

ProjectId =2,ProjectName = null, Customer = "c2", Address = null , 和

lst3包含Person具有 ProjectId 和 Address 的对象

ProjectId = 1, ProjectName = null, Customer =null, Address = "a1"

ProjectId = 2, ProjectName = null, Customer =null, Address = "a2".

考虑到每个列表中有多个这样的记录,并且 ProjectId 对于每个项目都是唯一的,我如何合并/组合这些列表以获得一个包含合并对象的列表

ProjectId=1, ProjectName="X", Customer="c1", address="a1"

ProjectId=2, ProjectName="Y", Customer="c2", address="a2"

我发现这些链接相似并尝试使用它但无法满足结果

使用 linq 从两个对象列表创建列表

如何使用 LINQ 合并两个列表?

谢谢你。

4

6 回答 6

3

这可以通过非常简单的多步骤方法完成。首先,定义一个Func<Project, Project, Project>来处理实际的记录合并。也就是说,您正在定义一个签名等效于public Project SomeMethod(Project p1, Project p2). 此方法实现了您上面概述的合并逻辑。接下来,我们将列表的元素连接在一起,然后将它们分组ProjectId,使用我们的合并委托作为重载中的聚合函数,该函数GroupBy接受结果选择器:

Func<Project, Project, Project> mergeFunc = (p1,p2) => new Project
    {
        ProjectId = p1.ProjectId,
        ProjectName = p1.ProjectName == null ? p2.ProjectName : p1.ProjectName,
        Customer = p1.Customer == null ? p2.Customer : p1.Customer,
        Address = p1.Address == null ? p2.Address : p1.Address    
    };

var output = lst1.Concat(lst2).Concat(lst3)
                 .GroupBy(x => x.ProjectId, (k, g) => g.Aggregate(mergeFunc)); 

这是对上述逻辑以及输出的快速而肮脏的测试:

List<Project> lst1; List<Project> lst2; List<Project> lst3;
lst1 = new List<Project> 
    {
        new Project { ProjectId = 1, ProjectName = "P1" },
        new Project { ProjectId = 2, ProjectName = "P2" },
        new Project { ProjectId = 3, ProjectName = "P3" }
    };
lst2 = new List<Project>
    {
        new Project { ProjectId = 1, Customer = "Cust1"},
        new Project { ProjectId = 2, Customer = "Cust2"},
        new Project { ProjectId = 3, Customer = "Cust3"}
    };
lst3 = new List<Project>
    {
        new Project { ProjectId = 1, Address = "Add1"},
        new Project { ProjectId = 2, Address = "Add2"},
        new Project { ProjectId = 3, Address = "Add3"}
    };

Func<Project, Project, Project> mergeFunc = (p1,p2) => new Project
    {
        ProjectId = p1.ProjectId,
        ProjectName = p1.ProjectName == null ? p2.ProjectName : p1.ProjectName,
        Customer = p1.Customer == null ? p2.Customer : p1.Customer,
        Address = p1.Address == null ? p2.Address : p1.Address    
    };

var output = lst1
    .Concat(lst2)
    .Concat(lst3)
    .GroupBy(x => x.ProjectId, (k, g) => g.Aggregate(mergeFunc));

IEnumerable<bool> assertedCollection = output.Select((x, i) => 
    x.ProjectId == (i + 1) 
    && x.ProjectName == "P" + (i+1) 
    && x.Customer == "Cust" + (i+1) 
    && x.Address == "Add" + (i+1));

Debug.Assert(output.Count() == 3);  
Debug.Assert(assertedCollection.All(x => x == true));

- - 输出 - -

IEnumerable<Project> (3 items)   
ProjectId ProjectName Customer Address 
1 P1 Cust1 Add1 
2 P2 Cust2 Add2 
3 P3 Cust3 Add3 
于 2012-05-29T14:29:26.883 回答
2

使用查找,您可以这样做:

        List<Project> lst = lst1.Union(lst2).Union(lst3).ToLookup(x => x.ProjectId).Select(x => new Project()
        {
            ProjectId = x.Key,
            ProjectName = x.Select(y => y.ProjectName).Aggregate((z1,z2) => z1 ?? z2),
            Customer = x.Select(y => y.Customer).Aggregate((z1, z2) => z1 ?? z2),
            Address = x.Select(y => y.Address).Aggregate((z1, z2) => z1 ?? z2)
        }).ToList();
于 2012-05-29T12:45:42.477 回答
1

我假设该列表包含相同数量的项目并按 ProjectId 排序。

List<Project> lst1; List<Project> lst2; List<Project> lst3

如果列表未排序,您可以先对其进行排序。

list1.Sort(p => p.ProjectId);
list2.Sort(p => p.ProjectId);
list3.Sort(p => p.ProjectId);

用于合并对象

List<Project> list4 = new List<Project>();
for(int i=1; i<list.Count; i++)
{ 
    list4.Add(new Project
    {
       ProjectId = list1[i].ProjectId;
       ProjectName = list1[i].ProjectName;
       Customer = list2[i].Customer;
       Address = list3[i].Address;
    });

}
于 2012-05-29T12:19:18.027 回答
1

我相信以下是 LINQ 的Join工作原理:

var mergedProjects =
    lst1
        .Join(lst2,
            proj1 => proj1.ProjectID,
            proj2 => proj2.ProjectID,
            (proj1, proj2) => new { Proj1 = proj1, Proj2 = proj2 })
        .Join(lst3,
            pair => pair.Proj1.ProjectID,
            proj3 => proj3.ProjectID,
            (pair, proj3) => new Project
            {
                ProjectID = proj3.ProjectID,
                ProjectName = pair.Proj1.ProjectName,
                Customer = pair.Proj2.Customer,
                Address = proj3.Address
            });

如果在所有三个列表ProjectID中都找不到 ,这将不会返回任何结果。

如果这是一个问题,我认为您最好手动执行此操作而不是使用 LINQ。

于 2012-05-29T12:23:19.410 回答
1

虽然矫枉过正,但我​​很想将其作为扩展方法:

public static List<T> MergeWith<T,TKey>(this List<T> list, List<T> other, Func<T,TKey> keySelector, Func<T,T,T> merge)
{
    var newList = new List<T>();
    foreach(var item in list)
    {
        var otherItem = other.SingleOrDefault((i) => keySelector(i).Equals(keySelector(item)));
        if(otherItem != null)
        {
            newList.Add(merge(item,otherItem));
        }
    }
    return newList;
}

用法将是:

var merged = list1
     .MergeWith(list2, i => i.ProjectId,
       (lhs,rhs) => new Project{ProjectId=lhs.ProjectId,ProjectName=lhs.ProjectName, Customer=rhs.Customer})
    .MergeWith(list3,i => i.ProjectId,
       (lhs,rhs) => new Project{ProjectId=lhs.ProjectId,ProjectName=lhs.ProjectName, Customer=lhs.Customer,Address=rhs.Address});

现场示例:http ://rextester.com/ETIVB14254

于 2012-05-29T12:36:21.470 回答
0

这是假设您要获取第一个非空值,或恢复为默认值 - 在本例中为字符串为空。

private static IEnumerable<Project> GetMergedProjects(IEnumerable<List<Project>> projects)
{
    var projectGrouping = projects.SelectMany(p => p).GroupBy(p => p.ProjectId);
    foreach (var projectGroup in projectGrouping)
    {
        yield return new Project
                            {
                                ProjectId = projectGroup.Key,
                                ProjectName =
                                    projectGroup.Select(p => p.ProjectName).FirstOrDefault(
                                        p => !string.IsNullOrEmpty(p)),
                                Customer =
                                    projectGroup.Select(c => c.Customer).FirstOrDefault(
                                        c => !string.IsNullOrEmpty(c)),
                                Address =
                                    projectGroup.Select(a => a.Address).FirstOrDefault(
                                        a => !string.IsNullOrEmpty(a)),
                            };
    }
}

如果需要,您也可以将此作为扩展方法。

于 2012-05-29T13:08:23.510 回答