Запрос 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