Анонимный тип и пересечение 2 списков - PullRequest
2 голосов
/ 14 сентября 2010
public class thing
{
public int Id{get;set;}
public decimal shouldMatch1 {get;set;}
public int otherMatch2{get;set;}
public string doesntMatter{get;set;}
public int someotherdoesntMatter{get;set;}
}
List<thing> firstList = new List<thing>();
List<thing> secondList = new List<thing>();

firstList.Add( new thing{ Id=1,shouldMatch1 = 1.11M, otherMatch2=1000,doesntMatter="Some fancy string", someotherdoesntMatter=75868});
firstList.Add( new thing{ Id=2,shouldMatch1 = 2.22M, otherMatch2=2000,doesntMatter="Some fancy string", someotherdoesntMatter=65345});
firstList.Add( new thing{ Id=3,shouldMatch1 = 3.33M, otherMatch2=3000,doesntMatter="Some fancy string", someotherdoesntMatter=75998});
firstList.Add( new thing{ Id=4,shouldMatch1 = 4.44M, otherMatch2=4000,doesntMatter="Some fancy string", someotherdoesntMatter=12345});

secondList.Add( new thing{ Id=100,shouldMatch1 = 1.11M, otherMatch2=1000,doesntMatter="Some fancy string", someotherdoesntMatter=75868});
secondList.Add( new thing{ Id=200,shouldMatch1 = 2.22M, otherMatch2=200,doesntMatter="Some fancy string", someotherdoesntMatter=65345});
secondList.Add( new thing{ Id=300,shouldMatch1 = 3.33M, otherMatch2=300,doesntMatter="Some fancy string", someotherdoesntMatter=75998});
secondList.Add( new thing{ Id=400,shouldMatch1 = 4.44M, otherMatch2=4000,doesntMatter="Some fancy string", someotherdoesntMatter=12345});
//Select new firstList.Id,secondList.Id where firstList.shouldMatch1 ==secondList.shouldMatch1  && firstList.otherMatch2==secondList.otherMatch2

//SHould return 
//1,100
//4,400

Есть ли способ пересечения списков, или я должен их повторить?

Псевдокод

    firstList.Intersect(secondList).Where(firstList.shouldMatch1 == secondList.shouldMatch1 && firstList.otherMatch2 == secondList.otherMatch2)
Select new {Id1=firstList.Id,Id2=secondList.Id};

С уважением

_Eric

Ответы [ 4 ]

4 голосов
/ 14 сентября 2010

Вы можете использовать подход, отличный от пересечения и реализации IEqualityComparer, следующим образом:

var query = from f in firstList
            from s in secondList
            where f.shouldMatch1 == s.shouldMatch1 &&
                  f.otherMatch2 == s.otherMatch2
            select new { FirstId = f.Id, SecondId = s.Id };

foreach (var item in query)
    Console.WriteLine("{0}, {1}", item.FirstId, item.SecondId);

По сути, это метод Enumerable.SelectMany в формате запроса.Объединение, скорее всего, будет быстрее, чем этот подход.

2 голосов
/ 14 сентября 2010

Подумайте об объединении нескольких условий для объединения ваших записей.Пересечение может привести к потере идентификаторов слева или справа.

Вот пример рабочего соединения из нескольких столбцов для этого конкретного сценария.Привлекательность этого запроса заключается в том, что он не требует сравнения равенств и позволяет извлекать столбец идентификатора при объединении с другими указанными столбцами.

var query = from first in firstList
            join second in secondList on
                new { first.shouldMatch1, first.otherMatch2 }
                    equals
                new { second.shouldMatch1, second.otherMatch2 }
            select new
            {
                FirstId = first.Id,
                SecondId = second.Id
            };
1 голос
/ 14 сентября 2010

Вам необходимо сделать переопределение thing типа Equals и GetHashCode, чтобы указать его семантику равенства:

public sealed class Thing : IEquatable<Thing>
{
    public int Id{get;set;}
    public decimal ShouldMatch1 {get;set;}
    public int OtherMatch2{get;set;}
    public string DoesntMatter{get;set;}
    public int SomeOtherDoesntMatter{get;set;}

    public override int GetHashCode()
    {
        int hash = 17;
        hash = hash * 31 + ShouldMatch1.GetHashCode() ;
        hash = hash * 31 + OtherMatch2.GetHashCode() ;
        return hash;
    }

    public override bool Equals(object other) {
        return Equals(other as Thing);
    }

    public bool Equals(Thing other) {
        if (other == null) {
            return false;
        }
        return ShouldMatch1 == other.ShouldMatch1 &&
               OtherMatch2 == other.OtherMatch2;
    }
}

Обратите внимание, что запечатывание класса упрощает проверку на равенство. Также обратите внимание, что если вы поместите один из них в словарь в качестве ключа, но затем измените Id, ShouldMatch1 или OtherMatch2, вы не сможете найти его снова ...

Теперь, если вы используете настоящий анонимный тип, вам не удастся это сделать ... и сложно реализовать IEqualityComparer<T> для передачи Intersect, когда он анонимный. Вы можете написать метод IntersectBy, немного похожий на метод DisinctBy от MoreLINQ ... это, вероятно, самый чистый подход, если вы действительно используете анонимный тип.

Вы бы использовали это так:

var query = first.Intersect(second);

Затем вы получите IEnumerable<Thing>, из которого вы сможете получить нужные биты.

Другой вариант - использовать объединение:

var query = from left in first
            join right in second 
            on new { left.ShouldMatch1, left.OtherMatch2 } equals
               new { right.ShouldMatch1, right.OtherMatch2 }
            select new { left, right };

(РЕДАКТИРОВАТЬ: Я только что заметил, что другие тоже сделали объединение ... ну хорошо.)

Все же другой вариант , если вас интересуют только биты совпадения, - это проецирование последовательностей:

var query = first.Select(x => new { x.ShouldMatch1, x.OtherMatch2 })
                 .Intersect(second.Select(x => new { x.ShouldMatch1, 
                                                     x.OtherMatch2 }));
0 голосов
/ 14 сентября 2010

Вам понадобится компаратор равенства:

public class thingEqualityComparer : IEqualityComparer<thing>
{
    #region IEqualityComparer<thing> Members

    public bool Equals(thing x, thing y) {
        return (x.shouldMatch1 == y.shouldMatch1 && x.otherMatch2 == y.otherMatch2) 

    public int GetHashCode(thing obj) {
            // if this does not suffice provide a better implementation.
        return  obj.GetHashCode();
    }

    #endregion
}

Затем вы можете пересечь коллекции с помощью:

firstList.Intersect(secondList, new thingEqualityComparer());

Кроме того, вы можете переопределить функцию Equal (см. Решение Джона).

Также обратите внимание, что thing не является анонимным классом - это будет, например, new { prop = 1 }.

...