我有一个Project class
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
包含带有ProjectId和ProjectName的Person
个对象。
ProjectId =1, ProjectName = "X", Customer = null, Address = null
ProjectId =2, ProjectName = "Y", Customer = null, Address = null
lst2
包含带有ProjectId和Customer
Person
个对象
ProjectId =1,ProjectName = null, Customer = "c1", Address = null
ProjectId =2,ProjectName = null, Customer = "c2", Address = null
,和
lst3
包含带有ProjectId和地址
Person
个对象
ProjectId = 1, ProjectName = null, Customer =null, Address = "a1"
ProjectId = 2, ProjectName = null, Customer =null, Address = "a2"
。
考虑到每个列表中有多个这样的记录,而ProjectId对于每个项目都是Uniqe,如何合并/组合这些列表以获得一个包含合并对象的列表
ProjectId=1, ProjectName="X", Customer="c1", address="a1"
ProjectId=2, ProjectName="Y", Customer="c2", address="a2"
我发现这些链接类似并尝试过但无法满足结果
Create a list from two object lists with linq
How to merge two lists using LINQ?
谢谢。
答案 0 :(得分:2)
使用Lookup你可以这样做:
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();
答案 1 :(得分:2)
这可以通过简单的多步骤方法完成。首先,定义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
答案 2 :(得分: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;
});
}
答案 3 :(得分: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。
答案 4 :(得分: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});
答案 5 :(得分:0)
这假设你想要获取第一个非空值,或者恢复为默认值 - 在这种情况下为字符串的null。
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)),
};
}
}
如果需要,您也可以将其作为扩展方法。