使用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比较两个数组的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript单行代码,也就是代码片段

如何比较两个字符串数组的序列

两个string数组对应比较

使用 Linq 和 Regex 比较 2 个字符串数组以进行部分匹配

比较两个字典并使用 Linq 确定最小值

比较两个列表并使用 linq 返回不匹配的项目