繁体   English   中英

使用LINQ比较两个数组

[英]Compare two arrays using 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);

我想要来自arrayOnearrayTwo中不存在。 所以在这里我需要结果为: 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对象缺点:需要编码

一种方法是包括索引以及:

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"}

您可以通过向数组的每个元素添加索引来获得所需的输出,以使它们看起来像

{{ "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);

由于不需要最终输出的顺序,则可以向上组在重复的字符串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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM