根据 属性 将两个列表合二为一
Combine two lists into one based on property
请问是否有一种优雅高效的方式将两个 MyClass 列表合并为一个?
MyClass 看起来像这样:
- ID:
int
- 姓名:
string
- 分机 ID:
int?
列表是从不同的来源填充的,列表中的对象共享 ID,因此看起来像这样:
MyClass instance from List1
ID = someInt
Name = someString
ExtID = null
和 List2 中的 MyClass 实例
ID = someInt (same as List1)
Name = someString (same as List1)
ExtID = someInt
我基本上需要的是合并这两个列表,所以结果是一个包含以下内容的列表:
ID = someInt (from List1)
Name = someString (from List1)
ExtID = someInt (null if no corresponding item - based on ID - on List2)
我知道我可以简单地使用 foreach 循环来做到这一点,但我很想知道是否有更优雅且可能更受欢迎(由于性能、可读性)的方法?
有很多方法取决于优先级,例如。合并 + 查找:
//this will create a key value pairs: id -> matching instances
var idMap = list1.Union(list2).ToLookup(myClass => myClass.ID);
//now just select for each ID the instance you want, ex. with some value
var mergedInstances = idMap.Select(row =>
row.FirstOrDefault(myClass => myClass.ExtId.HasValue) ?? row.First());
上面的好处是它可以处理任何数量的任何列表,即使它们包含许多重复的实例,然后您可以轻松修改合并条件
一个小的改进是提取一个方法来合并实例:
MyClass MergeInstances(IEnumerable<MyClass> instances){
return instances.FirstOrDefault(myClass => myClass.ExtId.HasValue)
?? instances.First(); //or whatever else you imagine
}
现在只需在上面的代码中使用它
var mergedInstances = idMap.Select(MergeInstances);
干净、灵活、简单、无附加条件。性能方面并不完美,但谁在乎呢。
编辑:由于性能是优先考虑的,所以有更多选择
像上面那样进行查找,但只针对较小的列表。然后遍历更大的并进行所需的更改 O(m log m) + O(n)。 m - 较小的列表大小,n - 较大的列表大小 - 应该是最快的。
按元素 ID 对两个列表进行排序。创建一个 for 循环,循环遍历它们,将当前索引保持到两个列表具有相同 id 的元素。将索引移动到两个列表中找到的下一个最小的 id,如果只有一个,则只移动它。 O(n log n) + O(m log m) + O(n);
我建议在 class 的方法中创建 foreach 循环,所以每次你需要做这样的事情时,你都会使用类似
的东西
instanceList1.MergeLists(instanceList2)
并且使用这种方法,您可以通过合并操作控制您想要的一切。
这是你想要的吗
var joined = from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
select new MyClass(Item1.Id, Item1.Name, Item1.ExtID??Item2.ExtID);
编辑:如果您正在寻找外部联接,
var query = from Item1 in list1
join Item2 in list2 on Item1.Id equals Item2.Id into gj
from sublist2 in gj.DefaultIfEmpty()
select new MyClass(Item1.Id, Item1.Name, sublist2??string.empty);
在可读性方面,使用 foreach 循环是个不错的主意..
请问是否有一种优雅高效的方式将两个 MyClass 列表合并为一个?
MyClass 看起来像这样:
- ID:
int
- 姓名:
string
- 分机 ID:
int?
列表是从不同的来源填充的,列表中的对象共享 ID,因此看起来像这样:
MyClass instance from List1
ID = someInt
Name = someString
ExtID = null
和 List2 中的 MyClass 实例
ID = someInt (same as List1)
Name = someString (same as List1)
ExtID = someInt
我基本上需要的是合并这两个列表,所以结果是一个包含以下内容的列表:
ID = someInt (from List1)
Name = someString (from List1)
ExtID = someInt (null if no corresponding item - based on ID - on List2)
我知道我可以简单地使用 foreach 循环来做到这一点,但我很想知道是否有更优雅且可能更受欢迎(由于性能、可读性)的方法?
有很多方法取决于优先级,例如。合并 + 查找:
//this will create a key value pairs: id -> matching instances
var idMap = list1.Union(list2).ToLookup(myClass => myClass.ID);
//now just select for each ID the instance you want, ex. with some value
var mergedInstances = idMap.Select(row =>
row.FirstOrDefault(myClass => myClass.ExtId.HasValue) ?? row.First());
上面的好处是它可以处理任何数量的任何列表,即使它们包含许多重复的实例,然后您可以轻松修改合并条件
一个小的改进是提取一个方法来合并实例:
MyClass MergeInstances(IEnumerable<MyClass> instances){
return instances.FirstOrDefault(myClass => myClass.ExtId.HasValue)
?? instances.First(); //or whatever else you imagine
}
现在只需在上面的代码中使用它
var mergedInstances = idMap.Select(MergeInstances);
干净、灵活、简单、无附加条件。性能方面并不完美,但谁在乎呢。
编辑:由于性能是优先考虑的,所以有更多选择
像上面那样进行查找,但只针对较小的列表。然后遍历更大的并进行所需的更改 O(m log m) + O(n)。 m - 较小的列表大小,n - 较大的列表大小 - 应该是最快的。
按元素 ID 对两个列表进行排序。创建一个 for 循环,循环遍历它们,将当前索引保持到两个列表具有相同 id 的元素。将索引移动到两个列表中找到的下一个最小的 id,如果只有一个,则只移动它。 O(n log n) + O(m log m) + O(n);
我建议在 class 的方法中创建 foreach 循环,所以每次你需要做这样的事情时,你都会使用类似
的东西instanceList1.MergeLists(instanceList2)
并且使用这种方法,您可以通过合并操作控制您想要的一切。
这是你想要的吗
var joined = from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
select new MyClass(Item1.Id, Item1.Name, Item1.ExtID??Item2.ExtID);
编辑:如果您正在寻找外部联接,
var query = from Item1 in list1
join Item2 in list2 on Item1.Id equals Item2.Id into gj
from sublist2 in gj.DefaultIfEmpty()
select new MyClass(Item1.Id, Item1.Name, sublist2??string.empty);
在可读性方面,使用 foreach 循环是个不错的主意..