LINQ合并2个列表,保持顺序和来源[重复]
Posted
技术标签:
【中文标题】LINQ合并2个列表,保持顺序和来源[重复]【英文标题】:LINQ merging 2 lists, keeping seqeunce and origin [duplicate] 【发布时间】:2019-06-08 01:57:50 【问题描述】:这里我有 2 个相同对象类型的列表。
object = id: xxx, ... // attribute "id" is used to find the identical obj
List oldSet = [old1, old2, old3];
List newSet = [new2, new3, new4];
// old2 = id= 2, result = 5, ...
// new2 = id= 2, result = 1, ...
// expected result = oldSet: old2; newSet: new2
我想合并两个列表,同时保留它来自哪个列表的来源。
预期结果如下:
List mergedSet = [old1, null, old2, new2, old3, new3, null, new4];
我正在考虑使用 LINQ C#,但卡在某个地方。
请多多指教。
谢谢! :)
【问题讨论】:
听说过字典吗?还是多维数组?mergedSet
的Type
是什么?是List<Tuple<Item, Item>>
吗?
我认为结果类型可以是可选的。只要我能分辨出这个项目来自 oldSet 或 newSet,并将具有相同 id 的项目组合起来。谢谢你的提示
如果您能提供minimal reproducible example,那就太好了。您的代码按原样无法编译。
您的问题中缺少以下详细信息以提供有用的答案:(1)oldSet
和newSet
将具有相同顺序的项目,即如果项目的id
在同一个索引中不匹配是否可以安全地假设该项目不存在于另一个列表中? (2)应该保持谁的顺序?即,如果两个列表的所有内容都不同,并且任何项目都没有单一匹配项,那么输出应该是什么?
【参考方案1】:
这里有一些代码可以用 Linq 做你想做的事。它基本上遍历所有旧列表,并通过从新列表中查找匹配项将对添加到合并列表中(如果未找到匹配项,则添加 null
作为第二项)。然后它遍历新列表中的剩余项目,并将它们与null
添加为第一个项目。它选择具有两个属性的动态类型:OldSet
和 NewSet
,因此您知道每个项目的来源。
合并代码很简单:
var mergedSet = oldSet.Select(o =>
new OldSet = o, NewSet = newSet.FirstOrDefault(n => n.id == o.id))
.Concat(newSet.Where(n => oldSet.All(o => o.id != n.id)).Select(n =>
new OldSet = (Item) null, NewSet = n));
这基于以下项目类别:
class Item
public int id get; set;
public string result get; set;
public override string ToString()
return $"resultid";
我们创建我们的列表:
List<Item> oldSet = new List<Item>
new Item id = 1, result = "old",
new Item id = 2, result = "old",
new Item id = 3, result = "old",
;
List<Item> newSet = new List<Item>
new Item id = 2, result = "new",
new Item id = 3, result = "new",
new Item id = 4, result = "new",
;
运行合并代码(首先是sn-p),然后显示结果:
foreach (var item in mergedSet)
Console.WriteLine($"item.NewSet,item.OldSet");
输出
【讨论】:
这个任务的代码太多了.... Linq 专门用来做这种事情。在这里使用元组没有意义,几个连接或联合就可以了。 你是对的,我更正了我的代码,删除了不相关的 cmets... :) @DrunkenCodeMonkey 酷。我还纠正了我的错误,将合并代码从 2 行减少到 1 行(并且还使用了动态类型),以防有人投反对票。【参考方案2】:试试这样的:
List<string> oldSet = new List<string>() "old1", "old2", "old3";
List<string> newSet = new List<string>() "new2", "new3", "new4";
var results = oldSet.Select((x,i) => new oldSet = x, newSet = newSet[i]).ToList();
【讨论】:
这个问题有点破,但似乎他不只是压缩它们,而是根据数据中的数字进行配对。【参考方案3】:您可以离开加入这两个列表。我编辑了答案,因为您实际上需要左加入两次,联合,并应用选择不同来获得 oldSet = null 且没有重复的情况...
var mergedSet = (from o in oldSet
join n in newSet on o.id equals n.id into ns
from n in ns.DefaultIfEmpty()
select new OldSet = o, NewSet = n )
.Union(from n in newSet
join o in oldSet on n.id equals o.id into os
from o in os.DefaultIfEmpty()
select new OldSet = o, NewSet = n )
.Distinct();
【讨论】:
【参考方案4】:可能有点矫枉过正,但如果你真的想使用 LINQ
List<Item> oldSet = new List<Item>
new Item id = 1, result = "old",
new Item id = 2, result = "old",
new Item id = 3, result = "old",
;
List<Item> newSet = new List<Item>
new Item id = 2, result = "new",
new Item id = 3, result = "new",
new Item id = 4, result = "new",
;
var resultL = oldSet.GroupJoin(
newSet,
o => o.id,
n => n.id,
(o,n) => new Old = o, New = n )
.SelectMany(
n => n.New.DefaultIfEmpty(),
(o,n) => new Tuple<Item,Item>(o.Old,n));
var resultR= newSet.GroupJoin(
oldSet,
n => n.id,
o=> o.id,
(n,o) => new Old = o, New = n )
.SelectMany(
o=> o.Old.DefaultIfEmpty(),
(n,o) => new Tuple<Item,Item>(o,n.New));
var result = resultL.Union(resultR).Distinct();
【讨论】:
【参考方案5】:在这种情况下,您必须使用两个GroupJoin
和Union
的结果。
看下面的代码:
var res1 = oldSet.GroupJoin(newSet, o => o, k => k, (x, y) => var yy = y.FirstOrDefault(); return new X = x, Y = yy ; );
var res2 = newSet.GroupJoin(oldSet, o => o, k => k, (x, y) => var yy = y.FirstOrDefault(); return new X = yy, Y = x ; );
var result = res1.Union(res2).ToList();// Your result is here
【讨论】:
以上是关于LINQ合并2个列表,保持顺序和来源[重复]的主要内容,如果未能解决你的问题,请参考以下文章
C# LINQ 组按属性集合,然后按列表定义的显式顺序对每个组进行排序[重复]
保持地图的顺序与[重复]之前的arrayList中的顺序相同