Запрос LINQ с Distinct и Union

В настоящее время у меня есть 2 запроса, которые возвращают списки MyModel следующим образом:

var q1 = ....
         select new MyModel()
         {
             TheData1 = ...
             TheData2 = ...
             TheUniqueID = ...
         }

var q2 = ....
         select new MyModel()
         {
             TheData1 = ...
             TheData2 = ...
             TheUniqueID = ...
         }

Если в q1 я:

TheUniqueID = 2,3,6,9,11 

и в q2 имеет:

TheUniqueID = 2,4,7,9,12

Как написать запрос, чтобы получить список MyModel, где

TheUniqueID = 2,3,4,6,7,9,11,12

Другими словами, каждый TheUniqueID присутствует только один раз (т.е. 2 и 9 не повторяется).

Я начал смотреть на Союз и отличался, но мне интересно, нужно ли мне 2 из утверждений или нет.

Любые предложения приветствуются.

Ответы

Ответ 1

Я думаю, что frenchie хочет получить список MyModel назад, а не только TheUniqueID.

Вам нужно создать класс MyModelTheUniqueIDComparer и передать новый экземпляр его в качестве второго аргумента в Union:

class MyModelTheUniqueIDComparer : IEqualityComparer<MyModel>
{
    public bool Equals(MyModel x, MyModel y)
    {
        return x.TheUniqueID == y.TheUniqueID;
    }

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects.

    public int GetHashCode(MyModel myModel)
    {
        return myModel.TheUniqueID.GetHashCode();
    }
}

Затем вы можете позвонить, чтобы получить результат:

var result = q1.Union(q2, new MyModelTheUniqueIDComparer());

Подробнее см. http://msdn.microsoft.com/en-us/library/bb358407.aspx.

Update:

Попробуйте следующее:

public class A
{
    public string TheData1 { get; set; }
    public string TheData2 { get; set; }
    public string UniqueID { get; set; }
}

public class AComparer : IEqualityComparer<A>
{

    #region IEqualityComparer<A> Members

    public bool Equals(A x, A y)
    {
        return x.UniqueID == y.UniqueID;
    }

    public int GetHashCode(A obj)
    {
        return obj.UniqueID.GetHashCode();
    }

    #endregion
}

И испытайте с этим:

var listOfA = new List<A>();
var q1 = from a in listOfA
                 select new A()
             {
                 TheData1 = "TestData",
                 TheData2 = "TestData",
                 UniqueID = a.UniqueID
             };

var anotherListOfA = new List<A>();
var q2 = from a in anotherListOfA
                 select new A()
                 {
                     TheData1 = "TestData",
                     TheData2 = "TestData",
                     UniqueID = a.UniqueID
                 };

q1.Union(q2, new AComparer());

Убедитесь, что у вас есть using System.Linq;

Ответ 2

Union создает Enumerable с уникальными значениями из обеих коллекций. Другими словами, вам не нужно Distinct.

edit: example of Union здесь

edit2: забыл, что это не список UniqueIDs, который вы конкатенируете. Я удалил предложенный код, поскольку он был неправильным. Вы должны сделать простой Union, если вы реализуете IEqualityComparer, но это может быть излишним.

Ответ 3

Как было указано, если вы комбинируете списки с .Union(), вам нужно будет определить уникальность, используя перегрузку, проходящую через IEqualityComparer для вашего типа.

var result = q1.Union(q2, myEqualityComparer);

в противном случае и проще использовать DistinctBy( x=> x.TheUniqueId) из проекта MoreLinq:

var result = q1.Concat(q2).DistinctBy(c => c.TheUniqueID);

Ответ 4

Неэффективный однострочный ответ без IEqualityComparerer

Используя исходный код MoreLinq в качестве вдохновения, это даст уникальный список:

Короткий ответ (OrderBy не нужен, но если он не используется, ответ выдается как 2,3,6,9,11,4,7,12):

var concattedUniqueList = theUniqueIDList1.Concat(theUniqueIDList2)
            .GroupBy(f=>f.UniqueID, f=>f).Select(g => g.First()).OrderBy(f=>f.UniqueID);

Полный ответ:

//INPUT
//theUniqueIDList1 = 2,3,6,9,11 
//theUniqueIDList2 = 2,4,7,9,12
//OUTPUT
//2,3,4,6,7,9,11,12
public class MyModel
{
    public string TheData1 { get; set; }
    public string TheData2 { get; set; }
    public int UniqueID { get; set; }
}

public static void GroupByEx1()
    {
        // Create a list of Models.
        List<MyModel> theUniqueIDList1 =
            new List<MyModel>{  new MyModel { TheData1="Barley",    UniqueID=2 },
                                    new MyModel { TheData1="Boots",     UniqueID=3 },
                                    new MyModel { TheData1="Whiskers",  UniqueID=6 },
                                    new MyModel { TheData1="Daisy",     UniqueID=9 },
                                    new MyModel { TheData1="Preti",     UniqueID=11 } };
        List<MyModel> theUniqueIDList2 =
            new List<MyModel>{  new MyModel { TheData1="Barley",    UniqueID=2 },
                                    new MyModel { TheData1="Henry",     UniqueID=4 },
                                    new MyModel { TheData1="Walsh",     UniqueID=7 },
                                    new MyModel { TheData1="Daisy",     UniqueID=9 },
                                    new MyModel { TheData1="Ugly",  UniqueID=12 } };

        var concattedUniqueList = theUniqueIDList1.Concat(theUniqueIDList2)
            .OrderBy(f=>f.UniqueID).GroupBy(f=>f.UniqueID, f=>f).Select(g => g.First());

        foreach (var item in concattedUniqueList)
        {
            Console.WriteLine("UniqueId: {0}({1})", item.UniqueID, item.TheData1);
        }
    }

void Main()
{
    GroupByEx1();               
    //2,3,4,6,7,9,11,12
}

Примечание: по сравнению с использованием IEqualityComparer для скорости - 10000 раз для каждого 698 нс для Concat 100 нс для IEqualityComparer

, разработанный в LinqPad