Как бы вы выполняли запрос "не в" с LINQ?
У меня есть две коллекции, которые имеют свойство Email
в обеих коллекциях. Мне нужно получить список элементов в первом списке, где Email
не существует во втором списке. С SQL я бы просто использовал "не в", но я не знаю эквивалента в LINQ. Как это делается?
Пока у меня есть соединение, например...
var matches = from item1 in list1
join item2 in list2 on item1.Email equals item2.Email
select new { Email = list1.Email };
Но я не могу присоединиться, так как мне нужна разница, и соединение завершится неудачно. Мне нужно каким-то образом использовать Содержит или Существует, я верю. Я еще не нашел примера, чтобы сделать это.
Ответы
Ответ 1
Я не знаю, поможет ли это вам, но..
NorthwindDataContext dc = new NorthwindDataContext();
dc.Log = Console.Out;
var query =
from c in dc.Customers
where !(from o in dc.Orders
select o.CustomerID)
.Contains(c.CustomerID)
select c;
foreach (var c in query) Console.WriteLine( c );
from Предложение NOT IN в LINQ to SQL Марко Руссо
Ответ 2
Вы хотите, кроме оператора.
var answer = list1.Except(list2);
Лучшее объяснение здесь: http://blogs.msdn.com/charlie/archive/2008/07/12/the-linq-set-operators.aspx
ПРИМЕЧАНИЕ. Этот метод лучше всего подходит только для примитивных типов, поскольку для реализации метода Except со сложными типами необходимо реализовать IEqualityComparer.
Ответ 3
в первом списке, где электронная почта не существует во втором списке.
from item1 in List1
where !(list2.Any(item2 => item2.Email == item1.Email))
select item1;
Ответ 4
Для людей, которые начинают с группы объектов в памяти и обращаются к базе данных, я нашел, что это лучший способ:
var itemIds = inMemoryList.Select(x => x.Id).ToArray();
var otherObjects = context.ItemList.Where(x => !itemIds.Contains(x.Id));
Это создает приятное предложение WHERE ... IN (...)
в SQL.
Ответ 5
Вы можете использовать комбинацию Where и Any для поиска не в:
var NotInRecord =list1.Where(p => !list2.Any(p2 => p2.Email == p.Email));
Ответ 6
Вы можете взять обе коллекции в двух разных списках, например list1 и list2.
Тогда просто напишите
list1.RemoveAll(Item => list2.Contains(Item));
Это будет работать.
Ответ 7
В случае, когда используется ADO.NET Entity Framework, решение EchoStorm также отлично работает. Но мне потребовалось несколько минут, чтобы обернуть мне голову. Предполагая, что у вас есть контекст базы данных, dc и хотите найти строки в таблице x, не связанные в таблице y, полный ответ ответа выглядит так:
var linked =
from x in dc.X
from y in dc.Y
where x.MyProperty == y.MyProperty
select x;
var notLinked =
dc.X.Except(linked);
В ответ на комментарий Энди, да, в запросе LINQ может быть два из них. Вот полный рабочий пример, используя списки. Каждый класс, Foo и Bar, имеет Id. Foo имеет ссылку "внешний ключ" на Bar через Foo.BarId. Программа выбирает все Foo, не связанные с соответствующей панелью.
class Program
{
static void Main(string[] args)
{
// Creates some foos
List<Foo> fooList = new List<Foo>();
fooList.Add(new Foo { Id = 1, BarId = 11 });
fooList.Add(new Foo { Id = 2, BarId = 12 });
fooList.Add(new Foo { Id = 3, BarId = 13 });
fooList.Add(new Foo { Id = 4, BarId = 14 });
fooList.Add(new Foo { Id = 5, BarId = -1 });
fooList.Add(new Foo { Id = 6, BarId = -1 });
fooList.Add(new Foo { Id = 7, BarId = -1 });
// Create some bars
List<Bar> barList = new List<Bar>();
barList.Add(new Bar { Id = 11 });
barList.Add(new Bar { Id = 12 });
barList.Add(new Bar { Id = 13 });
barList.Add(new Bar { Id = 14 });
barList.Add(new Bar { Id = 15 });
barList.Add(new Bar { Id = 16 });
barList.Add(new Bar { Id = 17 });
var linked = from foo in fooList
from bar in barList
where foo.BarId == bar.Id
select foo;
var notLinked = fooList.Except(linked);
foreach (Foo item in notLinked)
{
Console.WriteLine(
String.Format(
"Foo.Id: {0} | Bar.Id: {1}",
item.Id, item.BarId));
}
Console.WriteLine("Any key to continue...");
Console.ReadKey();
}
}
class Foo
{
public int Id { get; set; }
public int BarId { get; set; }
}
class Bar
{
public int Id { get; set; }
}
Ответ 8
var secondEmails = (from item in list2
select new { Email = item.Email }
).ToList();
var matches = from item in list1
where !secondEmails.Contains(item.Email)
select new {Email = item.Email};
Ответ 9
Пока Except
является частью ответа, это не весь ответ. По умолчанию Except
(например, несколько операторов LINQ) выполняет сравнительное сравнение ссылочных типов. Чтобы сравнить значения в объектах, вам нужно
- реализовать
IEquatable<T>
в вашем типе или
- переопределить
Equals
и GetHashCode
в вашем типе или
- передать экземпляр типа
IEqualityComparer<T>
для вашего типа
Ответ 10
Можно также использовать All()
var notInList = list1.Where(p => list2.All(p2 => p2.Email != p.Email));
Ответ 11
Пример использования списка int для простоты.
List<int> list1 = new List<int>();
// fill data
List<int> list2 = new List<int>();
// fill data
var results = from i in list1
where !list2.Contains(i)
select i;
foreach (var result in results)
Console.WriteLine(result.ToString());
Ответ 12
Для тех, кто также хочет использовать SQL-подобный оператор IN
в С#, загрузите этот пакет:
Mshwf.NiceLinq
Он имеет методы IN
и NotIn
:
var result = list1.In(x => x.Email, list2.Select(z => z.Email));
Даже вы можете использовать его таким образом
var result = list1.In(x => x.Email, "[email protected]", "[email protected]", "[email protected]");
Ответ 13
Спасибо, Брет. Ваше предложение тоже помогло мне. У меня был список объектов и я хотел отфильтровать его, используя другой список объектов.
Еще раз спасибо....
Если кому-то нужно, пожалуйста, посмотрите на мой пример кода:
'First, get all the items present in the local branch database
Dim _AllItems As List(Of LocalItem) = getAllItemsAtBranch(BranchId, RecordState.All)
'Then get the Item Mappings Present for the branch
Dim _adpt As New gItem_BranchesTableAdapter
Dim dt As New ds_CA_HO.gItem_BranchesDataTable
_adpt.FillBranchMappings(dt, BranchId)
Dim _MappedItems As List(Of LocalItem) = (From _item As LocalItem In _AllItems Join _
dr As ds_CA_HO.gItem_BranchesRow In dt _
On _item.Id Equals dr.numItemID _
Select _item).ToList
_AllItems = _AllItems.Except(_MappedItems.AsEnumerable).ToList
Return _AllItems
Ответ 14
Я не тестировал это с помощью LINQ в Entities:
NorthwindDataContext dc = new NorthwindDataContext();
dc.Log = Console.Out;
var query =
from c in dc.Customers
where !dc.Orders.Any(o => o.CustomerID == c.CustomerID)
select c;
В качестве альтернативы:
NorthwindDataContext dc = new NorthwindDataContext();
dc.Log = Console.Out;
var query =
from c in dc.Customers
where dc.Orders.All(o => o.CustomerID != c.CustomerID)
select c;
foreach (var c in query)
Console.WriteLine( c );
Ответ 15
Не удалось выполнить внешнее соединение, только выбрав элементы из первого списка, если группа пуста? Что-то вроде:
Dim result = (From a In list1
Group Join b In list2
On a.Value Equals b.Value
Into grp = Group
Where Not grp.Any
Select a)
Я не уверен, что это будет эффективно работать с инфраструктурой Entity.
Ответ 16
В качестве альтернативы вы можете сделать это так:
var result = list1.Where(p => list2.All(x => x.Id != p.Id));