使用LINQ比较两个数组
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用LINQ比较两个数组相关的知识,希望对你有一定的参考价值。
例如,我有两个数组:
string[] arrayOne = {"One", "Two", "Three", "Three", "Three"};
string[] arrayTwo = {"One", "Two", "Three"};
var result = arrayOne.Except(arrayTwo);
foreach (string s in result) Console.WriteLine(s);
我想要来自arrayOne
的物品,这些物品在arrayTwo
不存在。所以在这里我需要结果为:Three Three
但我没有得到任何结果,因为它将“三”视为常见而不检查其他两项(“三”,“三”)。
我不想写一个巨大的方法来解决这个问题。尝试了其他答案在SO但没有按预期工作:(。
谢谢!!!
构建第二个HashSet,然后过滤第一个只允许项目,如果你不能从HashSet中删除项目。
var hs = new HashSet<string>(arrayTwo);
var filtered = arrayOne.Where(item => !hs.Remove(item)).ToArray();
考虑到你在评论中的额外要求,一些漂亮的使用ILookup
在这里工作得很好。
var lookup1 = arrayOne.ToLookup(item => item);
var lookup2 = arrayTwo.ToLookup(item => item);
var output = lookup1.SelectMany(i => i.Take(i.Count() - lookup2[i.Key].Count())).ToArray();
答案取决于数组大小,重复元素计数,代码速度的重要性。
对于小型数组,以下代码将是最简单和最好的:
List<string> result = new List<string>(arrayOne);
foreach (string element in arrayTwo)
result.Remove(element);
如果你想要更大的数组效率,你可以使用消费者的答案。
如果您想要最有效的代码,则必须手动编写以下算法:1。对arrayOne和arrayTwo进行排序。 2.同时迭代两种算法(如在mergesort中)并省略具有相同元素的对。
Proc:没有沉重的Lookup对象缺点:需要编码
您可以通过向数组的每个元素添加索引来获得所需的输出,以使它们看起来像
{{ "One", 0 }, { "Two", 0 }, { "Three", 0 }, { "Three", 1 }, { "Three", 2 }}
{{ "One", 0 }, { "Two", 0 }, { "Three", 0 }}
然后你可以使用Except
删除重复项
var arrayOneWithIndex = arrayOne
.GroupBy(x => x)
.SelectMany(g => g.Select((e, i) => new { Value = e, Index = i }));
var arrayTwoWithIndex = arrayTwo
.GroupBy(x => x)
.SelectMany(g => g.Select((e, i) => new { Value = e, Index = i }));
var result = arrayOneWithIndex.Except(arrayTwoWithIndex).Select(x => x.Value);
一种方法是包括索引以及:
var result = arrayOne.Select((r, i) => new {Value = r, Index = i})
.Except(arrayTwo.Select((r, i) => new {Value = r, Index = i}))
.Select(t => t.Value);
这将为您输入所需的输出,但上述方法的问题是,不同索引上的相同字符串将被区别对待。
忽略指数的另一种方法可以这样做:
string[] arrayOne = { "One", "Two", "Three", "Three", "Three", "X" };
string[] arrayTwo = { "One", "Two", "Three" };
var query1 = arrayOne.GroupBy(r => r)
.Select(grp => new
{
Value = grp.Key,
Count = grp.Count(),
});
var query2 = arrayTwo.GroupBy(r => r)
.Select(grp => new
{
Value = grp.Key,
Count = grp.Count(),
});
var result = query1.Select(r => r.Value).Except(query2.Select(r => r.Value)).ToList();
var matchedButdiffferentCount = from r1 in query1
join r2 in query2 on r1.Value equals r2.Value
where r1.Count > r2.Count
select Enumerable.Repeat(r1.Value, r1.Count - r2.Count);
result.AddRange(matchedButdiffferentCount.SelectMany(r=> r));
result
将包含{"X", "Three", "Three"}
由于不需要最终输出的顺序,您可以在arrayOne
中对重复的字符串进行分组,并在arrayTwo
中分组减去计数(和当前)重复次数。然后,您可以再次展平集合,同时使用Enumerable.Repeat
复制迭代次数。
string[] arrayOne = {"One", "Two", "Three", "Three", "Three"};
string[] arrayTwo = {"One", "Two", "Three"};
var groupedTwo = arrayTwo
.GroupBy(g => g)
.ToDictionary(g => g.Key, g => g.Count());
var groupedResult = arrayOne
.GroupBy(a => a)
.Select(g => new {g.Key, Count = g.Count()})
.Select(g => new {g.Key, Residual = g.Count -
(groupedTwo.ContainsKey(g.Key) ? groupedTwo[g.Key] : 0)})
.SelectMany(g => Enumerable.Repeat(g.Key, g.Residual));
foreach (string s in groupedResult)
{
Console.WriteLine(s);
}
请注意,这显然不会保留原始顺序中可能发生的任何交错。
例如对于
string[] arrayOne = {"Three", "Four", "One", "Two", "Three", "Three"};
答案是不直观的
Three
Three
Four
迟到了这个讨论,并在此录制以供参考。 LINQ的Except方法使用默认的相等比较器来确定两个数组中哪些项匹配。在这种情况下,默认的相等比较器调用对象上的Equals方法。对于字符串,此方法已被重载以比较字符串的内容,而不是其标识(引用)。
这解释了为什么在这种特定情况下发生这种情况。当然,它没有提供解决方案,但我相信其他人已经提供了出色的答案。 (而且实际上,这比评论更适合我。)
我可能提出的一个建议是编写一个自定义比较器,并将其传递给接受一个的Except重载。自定义比较器并不过分复杂,但考虑到您的场景,我了解您可能不希望这样做的地方。
试试这个:
var result = from s in first
where !string.IsNullOrWhiteSpace(s) &&
!second.Contains(s)
select s;
好的,如果那不起作用 - 我更仔细地阅读了这些评论。
以下代码:
private static void Main(string[] args)
{
string[] first = {"One", "Two", "Three", "Three", "Three"};
string[] second = {"One", "Two", "Four", "Three"};
var result = FirstExceptSecond(first, second);
foreach (string s in result)
{
Console.WriteLine(s);
}
}
private static IEnumerable<string> FirstExceptSecond(IList<string> first, IList<string> second)
{
List<string> firstList = new List<string>(first);
List<string> secondList = second as List<string> ?? second.ToList();
foreach (string s in secondList)
{
if (firstList.Contains(s))
{
firstList.Remove(s);
}
}
return firstList;
}
产生以下结果:
Three
Three
使用LINQ比较数组相等性的另一种方法如下。
LINQ中使用的逻辑:在这段代码中,我过滤了第一个数组元素,使得第一个数组中的每个元素等于第二个数组中的对应元素,第一个数组的当前索引存在于第二个数组中;如果被比较的两个数组相等,那么这个过滤应该产生与第一个数组中相同数量的元素。
string[] arrayOne = {"One", "Two", "Three", "Three", "Three"};
string[] arrayTwo = {"One", "Two", "Three"};
bool result =(arrayOne.Where((string n, int i) => i <= (arrayTwo.Length-1) &&
n == arrayTwo[i]).Count() == arrayOne.Length);
//if result == true then arrays are equal else they are not
以上是关于使用LINQ比较两个数组的主要内容,如果未能解决你的问题,请参考以下文章