简体   繁体   English

使用LINQ比较两个数组

[英]Compare two arrays using LINQ

For example, I have two arrays: 例如,我有两个数组:

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

I want Items from arrayOne which are not there in arrayTwo . 我想要来自arrayOnearrayTwo中不存在。 So here I need result as: Three Three but I am getting no results as its treating "Three" as common and not checking the other two items("Three", "Three"). 所以在这里我需要结果为: Three Three但是我没有得到任何结果,因为它将“三”视为常见而不检查其他两项(“三”,“三”)。

I dont want to end up writing a huge method to solve this. 我不想写一个巨大的方法来解决这个问题。 Tried couple other answer on SO but didnt worked as expected :(. 尝试了其他答案在SO但没有按预期工作:(。

Thanks!!! 谢谢!!!

Build a HashSet of the second, and then filter the first only allowing items if you can't remove the item from the HashSet. 构建第二个HashSet,然后过滤第一个只允许项目,如果你不能从HashSet中删除项目。

 
 
 
  
  var hs = new HashSet<string>(arrayTwo); var filtered = arrayOne.Where(item => !hs.Remove(item)).ToArray();
 
  

Taking account of your extra requirements in the comments, some nifty use of ILookup works nicely here. 考虑到你在评论中的额外要求, 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();

The answer depends on array sizes, duplicate elements count, importance of code speed. 答案取决于数组大小,重复元素计数,代码速度的重要性。

For small arrays, the following code would be the simplest and the best: 对于小型数组,以下代码将是最简单和最好的:

List<string> result = new List<string>(arrayOne);
foreach (string element in arrayTwo)
    result.Remove(element);

If you want more efficiency for large arrays, you can use spender's answer. 如果你想要更大的数组效率,你可以使用消费者的答案。

If you want the most efficient code, you will have to code manually the following algorithm: 1. Sort both arrayOne and arrayTwo. 如果您想要最有效的代码,则必须手动编写以下算法:1。对arrayOne和arrayTwo进行排序。 2. Iterate over both algorithms simultaneously (like in mergesort) and omit pairs with the same elements. 2.同时迭代两种算法(如在mergesort中)并省略具有相同元素的对。

Proc: no heavy Lookup object Cons: need coding Proc:没有沉重的Lookup对象缺点:需要编码

One way to do it would be to include indices as well like: 一种方法是包括索引以及:

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

This will give you the required output for your input, but the issue with the above approach is, that, same string on different indices will be treated differently. 这将为您输入所需的输出,但上述方法的问题是,不同索引上的相同字符串将被区别对待。

The other approach to ignore indices could be done like: 忽略指数的另一种方法可以这样做:

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 will contain {"X", "Three", "Three"} result将包含{"X", "Three", "Three"}

You can get the desired output by adding an index to each element of the arrays to make them look like 您可以通过向数组的每个元素添加索引来获得所需的输出,以使它们看起来像

{{ "One", 0 }, { "Two", 0 }, { "Three", 0 }, { "Three", 1 }, { "Three", 2 }}
{{ "One", 0 }, { "Two", 0 }, { "Three", 0 }}

Then you can use Except to remove duplicates 然后,您可以使用“ 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);

Since the order of the final output isn't required, you could group up the repeated strings in arrayOne , and subtract, groupwise, the counted (and present) number of repeats in arrayTwo . 由于不需要最终输出的顺序,则可以向上组在重复的字符串arrayOne ,和减,成组,在重复的计数(和本)号码arrayTwo You can then flatten out the collections again, at the same time using Enumerable.Repeat to replicate out the number of iterations. 然后,您可以再次展开集合,同时使用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);
}

Note that this obviously won't preserve any interleaving which could occur in the original order. 请注意,这显然不会保留原始顺序中可能发生的任何交错。

eg For 例如

string[] arrayOne = {"Three", "Four", "One", "Two", "Three", "Three"};

The answer would unintuitively be 答案是不直观的

Three
Three
Four

Coming to this discussion late, and recording this here for reference. 迟到了这个讨论,并在此录制以供参考。 LINQ's Except method is using the default equality comparer to determine which items match in your two arrays. LINQ的Except方法使用默认的相等比较器来确定两个数组中哪些项匹配。 The default equality comparer, in this case, invokes the Equals method on the object. 在这种情况下,默认的相等比较器调用对象上的Equals方法。 For strings, this method has been overloaded to compare the content of the string, not its identity (reference). 对于字符串,此方法已被重载以比较字符串的内容 ,而不是其标识(引用)。

This explains why this is occurring in this particular scenario. 这解释了为什么在这种特定情况下发生这种情况。 Granted, it doesn't provide a solution, but I believe that others have already provided excellent answers. 当然,它没有提供解决方案,但我相信其他人已经提供了出色的答案。 (And realistically, this is more than I could fit into a comment.) (而且实际上,这比评论更适合我。)

One suggestion I might have made was to write a custom comparer, and passed it to the Except overload that accepts one. 我可能提出的一个建议是编写一个自定义比较器,并将其传递给接受一个的Except重载。 Custom comparers are not overly complicated, but given your scenario, I understand where you might not have desired to do so. 自定义比较器并不过分复杂,但考虑到您的场景,我了解您可能不希望这样做的地方。

Try this: 试试这个:

 
 
 
  
  var result = from s in first where !string.IsNullOrWhiteSpace(s) && !second.Contains(s) select s;
 
  

Ok if that didn't work -- I read the comments a bit more carefully. 好的,如果那不起作用 - 我更仔细地阅读了这些评论。

The following code: 以下代码:

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

Produces the following results: 产生以下结果:

Three
Three 

Another way you could compare equality of arrays using LINQ is as below. 使用LINQ比较数组相等性的另一种方法如下。

Logic used in LINQ : In this code, I am filtering the first array elements such that each element in first array is equal to corresponding element in second array and the current index of first array exists in second array; LINQ中使用的逻辑 :在这段代码中,我过滤了第一个数组元素,使得第一个数组中的每个元素等于第二个数组中的对应元素,第一个数组的当前索引存在于第二个数组中; if the two arrays being compared are equal then this filtering should result in the same number of elements as there are in the first array. 如果被比较的两个数组相等,那么这个过滤应该产生与第一个数组中相同数量的元素。

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