我想问一下是否有一种优雅而有效的方式将两个MyClass列表合并为一个?
MyClass看起来像这样:
ID: int 名称: string ExtID: int?并且列表从不同的来源填充,列表中的对象共享ID,因此它看起来像这样:
MyClass instance from List1 ID = someInt Name = someString ExtID = nullList2中的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循环来做到这一点,但我很想知道是否有更优雅或者更可取的方法(由于性能和可读性)?
I would like to ask whether there's an elegant and efficient way to merge two lists of MyClass into one?
MyClass looks like this:
ID: int Name: string ExtID: int?and the lists are populated from different sources and objects in lists do share ID, so it looks like that:
MyClass instance from List1 ID = someInt Name = someString ExtID = nullAnd MyClass instance from List2
ID = someInt (same as List1) Name = someString (same as List1) ExtID = someIntWhat I basically need is to combine these two lists, so the outcome is a list containing:
ID = someInt (from List1) Name = someString (from List1) ExtID = someInt (null if no corresponding item - based on ID - on List2)I know I can do this simply using foreach loop, but I'd love to know if there's more elegant and maybe preferred (due to performance, readability) method?
最满意答案
取决于什么是优先级,有很多方法,例如。 联盟+查找:
//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的元素。 将索引移动到在这两个列表中找到的下一个最小的标识符,如果只有一个标识符,则只将其移入。 O(n log n)+ O(m log m)+ O(n);
There are many approaches depending on what is the priority, ex. Union + Lookup:
//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());The benefit of above is that it will work with whatever amount of whatever lists even if they contain many duplicated isntances and then you can easily modify the conditions of merging
A small improvement would be to extract a method to merge instances:
MyClass MergeInstances(IEnumerable<MyClass> instances){ return instances.FirstOrDefault(myClass => myClass.ExtId.HasValue) ?? instances.First(); //or whatever else you imagine }and now just use it in the code above
var mergedInstances = idMap.Select(MergeInstances);Clean, flexible, simple, no additional conditions. Performance wise not perfect, but who cares.
Edit: since performance is the priority, some more options
Do a lookup like above but only for the smaller list. Then iterate through the bigger and do the needed changes O(m log m) + O(n). m - smaller list size, n- bigger list size - should be fastest.
Order both lists by elements ids. Create a for loop, that iterates through both of them keeping current index to the element with same id for both lists. Move index to the next smallest id found in both list, if one has it only, move only this on. O(n log n) + O(m log m) + O(n);
更多推荐
发布评论