两个字符串数组的完全外连接
Posted
技术标签:
【中文标题】两个字符串数组的完全外连接【英文标题】:Full outer join of two string arrays 【发布时间】:2013-01-01 16:51:04 【问题描述】:给定以下两个字符串:
Dim str1() As String = "123", "456", "789", "0"
Dim str2() As String = "123", "456", "1"
如何执行 str1 和 str2 的完全外连接并最终得到如下结构:
Nothing, "1"
"0", Nothing
"123", "123"
"456", "456"
"789", Nothing
基于 SO 和其他网站上的几次讨论,我尝试使用 LINQ:
Dim v = From a In str1 Join b In str2 On a Equals b Group By a Into g = Group
From o In g.DefaultIfEmpty()
但是它并没有产生想要的结果,和这个完全一样(普通的INNER JOIN):
Dim v = From a In str1 Join b In str2 On a Equals b
我看到的最后一个例子是here (C#)。
这里是Another article,但它似乎太复杂了,不可能成为最短的解决方案(我见过更简单的 C# 示例,希望 VB 也能一样高效)。
为什么这个问题有用
例如,可以有一个文件结构,其中文件路径是一个键。通过对键进行完全外连接,您可以比较文件夹,查找哪一侧缺少哪些文件并向用户显示差异。任何类型的同步任务都可以使用这种方法。
【问题讨论】:
为什么这个问题可能不适用: 在某些情况下,这两个集合是排序的,例如许多文件系统将按字母顺序返回文件名。在这种情况下,遍历两个列表比较每个列表中的一个元素并在处理过程中处理不匹配比尝试所有可能的配对更有效。 @HABO:你说得对,IO.Directory.GetFiles
按字母顺序返回文件。它仍然有用的地方是后处理,例如,您不需要所有文件,只需要预先选择的文件。所以你只会在你感兴趣的 5% 的文件上调用GetFileInfo
。无论如何,我认为 LINQ 应该在内部对项目进行排序,然后执行连接,而不管输入如何。如果它不这样做,我会感到惊讶。
【参考方案1】:
我想这不是您想要的解决方案,但它似乎完成了任务:
string[] a1 = "123", "456", "1" ;
string[] a2 = "123", "456", "789", "0" ;
var intersection = a1.Intersect(a2); //get the intersection
var exceptions1 = a1.Except(a2); //get items from a1, that are not in a2
var exceptions2 = a2.Except(a1); //get items from a2, that are not in a1
var result = new List<Tuple<string, string>>(); //the result set
result.AddRange(intersection.Select(s => new Tuple<string, string>(s, s)));
result.AddRange(exceptions1.Select(s => new Tuple<string, string>(s, null)));
result.AddRange(exceptions2.Select(s => new Tuple<string, string>(null, s)));
foreach (var t in result)
Console.WriteLine((t.Item1 ?? "null") + "\t" + (t.Item2 ?? "null"));
输出是:
123 123
456 456
1 null
null 789
null 0
【讨论】:
所以基本上,您先进行 INNER JOIN、OUTER LEFT JOIN、OUTER RIGHT JOIN,然后将它们组合在一起?是的,我想这是一个有效的解决方法。 +1【参考方案2】:您可以使用HashSet,特别是IntersectWith
和SymmetricExceptWith
方法。
【讨论】:
是的,但它最终看起来几乎与@Konstantin Vasilcov 的答案一模一样;除了HashSet
s 而不是 LINQ
好的,现在我明白你的意思了。是的,hashset 对于大数据当然应该表现得更好。谢谢你提到它。 +1【参考方案3】:
好吧,这个答案与康斯坦丁的答案在功能上并没有什么不同,但它在空间上更小。
我所做的是将两个数组的外连接执行为元组并将两个 IEnumerable 合并。
var str1 = new string[] "123", "456", "789", "0" ;
var str2 = new string[] "123", "456", "1" ;
var unionedsets = (from a in str1 join b in str2 on a equals b into grp from g in grp.DefaultIfEmpty()
select new Tuple<string, string>(a, g)).Union(
from b in str2 join a in str1 on b equals a into grp from g in grp.DefaultIfEmpty()
select new Tuple<string,string>(g,b));
它看起来如此紧凑的唯一原因是因为我将 LINQ 语句组合在一起(这是一个技术术语吗?)。 Unsmooshed,代码如下所示:
var jstr1 = from a in str1
join b in str2 on a equals b
into grp
from g in grp.DefaultIfEmpty()
select new
Tuple<string, string>(a, g);
var jstr2 = from b in str2
join a in str1 on b equals a
into grp
from g in grp.DefaultIfEmpty()
select new
Tuple<string,string>(g,b);
var unionedjoin = jstr1.Union(jstr2);
这是否更接近您的目标?
【讨论】:
非常接近。但是你能转换成 VB.NET 吗?我的主要问题是为此目的在 VB.NET 中使用 INTO 语句。以上是关于两个字符串数组的完全外连接的主要内容,如果未能解决你的问题,请参考以下文章