С# Linq пересекается/за исключением одной части объекта

У меня есть класс:

class ThisClass
{
  private string a {get; set;}
  private string b {get; set;}
}

Я хотел бы использовать методы Intersect и Except Linq, т.е.:

private List<ThisClass> foo = new List<ThisClass>();
private List<ThisClass> bar = new List<ThisClass>();

Затем я заполняю два списка отдельно. Я хотел бы сделать, например (и я знаю, что это неправильно, просто псевдокод), следующее:

foo[a].Intersect(bar[a]);

Как бы я это сделал?


person David Archer    schedule 17.05.2012    source источник
comment
Что ты хочешь? Объясните словами, что вы хотите от этой строки foo[a].Intersect(bar[a]);.   -  person Nikhil Agrawal    schedule 17.05.2012


Ответы (6)


Может быть

// returns list of intersecting property 'a' values
foo.Select(f => f.a).Intersect(bar.Select(b => b.a));

Кстати, свойство a должно быть общедоступным.

person Sergey Berezovskiy    schedule 17.05.2012
comment
это возвращает коллекцию Property. Я искал возврат коллекции объектов. См. комментарий ниже от Patryk. - person Jakub Sluka; 06.01.2021

Если вам нужен список одного свойства, которое вы хотите пересечь, тогда все другие симпатичные решения LINQ работают отлично. НО! Если вы хотите пересечь весь класс и в результате получить List<ThisClass> вместо List<string>, вам придется написать свой собственный компаратор равенства.

foo.Intersect(bar, new YourEqualityComparer());

то же самое с Except.

public class YourEqualityComparer: IEqualityComparer<ThisClass>
{

    #region IEqualityComparer<ThisClass> Members


    public bool Equals(ThisClass x, ThisClass y)
    {
        //no null check here, you might want to do that, or correct that to compare just one part of your object
        return x.a == y.a && x.b == y.b;
    }


    public int GetHashCode(ThisClass obj)
    {
        unchecked
        {
            var hash = 17;
                            //same here, if you only want to get a hashcode on a, remove the line with b
            hash = hash * 23 + obj.a.GetHashCode();
            hash = hash * 23 + obj.b.GetHashCode();

            return hash;    
        }
    }

    #endregion
}
person Patryk Ćwiek    schedule 17.05.2012
comment
Это именно то, что я искал. Сравнение двух списков по 250 000 000 000 000 000 000 000 000 за несколько секунд. Спасибо! - person Jakub Sluka; 06.01.2021

Не уверен в скорости этого по сравнению с пересечением и сравнением, но как насчет:

//Intersect
var inter = foo.Where(f => bar.Any(b => b.a == f.a));
//Except - values of foo not in bar
var except = foo.Where(f => !bar.Any(b => b.a == f.a));
person Zach Johnson    schedule 14.01.2015
comment
Это алгоритм O(n * m), тогда как Intersect и Except оба являются O(n + m). Это делает вас намного хуже. Он также итерирует bar несколько раз, что может быть серьезной проблемой во всех видах ситуаций (он может давать разные результаты на каждой итерации, он может запрашивать базу данных или предварительно выполнять дорогостоящие вычисления на каждой итерации, он может иметь побочные эффекты, вызванные, когда повторный и т.д. - person Servy; 14.01.2015
comment
Расширение ответа @Servy: просто посмотрите на исходный код для оценки сложности методов Intersect / Except. - person vladimir; 07.05.2019

Каков именно желаемый эффект? Вы хотите получить список строк, состоящий из всех a в ваших классах, или список ThisClass, когда два экземпляра ThisClass идентифицируются с помощью уникальных значений a?

Если это первое, два ответа от @lazyberezovksy и @Tilak должны работать. Если это последнее, вам придется переопределить IEqualityComparer<ThisClass> или IEquatable<ThisClass>, чтобы Intersect знал, что делает два экземпляра ThisClass эквивалентными:

 private class ThisClass : IEquatable<ThisClass>
 {
     private string a;

     public bool Equals(ThisClass other)
     {
        return string.Equals(this.a, other.a);
     }
 }

то вы можете просто позвонить:

 var intersection = foo.Intersect(bar);     
person Avner Shahar-Kashtan    schedule 17.05.2012
comment
Вам всегда нужно переопределять GetHashCode при реализации IEquatable. Поскольку вы этого не сделаете, это не сработает. - person Servy; 14.01.2015

Я знаю, что это устарело, но не могли бы вы просто переопределить Equals и GetHashCode в самом классе?

class ThisClass
{
  public string a {get; set;}
  private string b {get; set;}

  public override bool Equals(object obj)
  {
    // If you only want to compare on a
    ThisClass that = (ThisClass)obj;
    return string.Equals(a, that.a/* optional: not case sensitive? */);
  }

  public override int GetHashCode()
  {
    return a.GetHashCode();
  }
}
person tyr    schedule 10.11.2016

Вы должны создать IEqualityComparer. Вы можете передать IEqualityComparer методу Intersect(). Это поможет вам легче получить список (который пересекается с полосой).

var intersectionList = foo.Intersect(bar, new ThisClassEqualityComparer()).ToList();


class ThisClassEqualityComparer : IEqualityComparer<ThisClass>
{

    public bool Equals(ThisClass b1, ThisClass b2)
    {
        return b1.a == b2.a;
    }


    public int GetHashCode(Box bx)
    {
       // To ignore to compare hashcode, please consider this.
       // I would like to force Equals() to be called
       return 0;
    }

}
person Pongsathon.keng    schedule 17.05.2012
comment
Вы не должны возвращать 0 из такого хеш-кода. Это полностью убьет производительность. Вместо этого вы должны использовать хэш-код a. - person Servy; 14.01.2015