LINQ ile “not in” sorgusu nasıl yapılır?


307

Her iki koleksiyonda da mülk Emailolan iki koleksiyonum var. Emailİkinci listede olmayan ilk listedeki öğelerin bir listesini almam gerekiyor . SQL ile sadece "değil" kullanırdım, ama LINQ eşdeğerini bilmiyorum. Bu nasıl yapılır?

Şimdiye kadar bir katılma var, sanki ...

var matches = from item1 in list1
join item2 in list2 on item1.Email equals item2.Email
select new { Email = list1.Email };

Ancak aradaki farkın olması gerektiğinden katılamam ve katılma başarısız olur. İnanıyorum İçerir veya Var olanları kullanmanın bir yoluna ihtiyacım var. Bunu yapmak için henüz bir örnek bulamadım.


3
Echostorm'un cevabının, Robert'inkinden daha net okunan bir kod ürettiğini lütfen unutmayın
Nathan Koop

Yanıtlar:


302

Bunun size yardımcı olup olmayacağını bilmiyorum ama ..

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 );

dan LINQ SQL için DEĞİL İÇİNDE maddesi ile Marco Russo


Ama ben varq için linq kullanın, bu yüzden "sadece ilkel türleri hata kullanılabilir" olsun. Etrafta iş var mı ...? manuel tekrarlama ve listeyi bulma dışında.
Acemi

13
Bu benim için LINQ to Entities ile iyi çalışıyor. SQL WHERE NOT EXISTS (alt sorgu) sorgusu haline gelir. Belki bunu ele alan bir güncelleme vardı?
scottheckel

2
Ben EF yeni sürümleri destek .Contains yapmak düşünüyorum artı bu soru .. bu yüzden soru kapsamına ihtiyaç olabilir ve burada .. cevap etiket EF (sürüm) veya LinqToSQL değil
Brett Caswell

4
@Robert Rouse - sql linq içinde cluse içinde değil bağlantısı artık çalışmıyor. Sadece bir fyi.
JonH

Sağlanan bağlantı, kötü amaçlı yazılım içerdiği belirtilen bir siteye yönlendiriyor.
mikesigs

334

İstisna operatörünü istiyorsunuz.

var answer = list1.Except(list2);

Burada daha iyi açıklama: https://docs.microsoft.com/archive/blogs/charlie/linq-farm-more-on-set-operators

NOT: Bu yöntem, yalnızca ilkel türler için en iyi sonucu verir, çünkü yöntemi karmaşık türlerle kullanmak için bir IEqualityComparer uygulamanız gerekir Except.


7
Dışında Kullanma: Karmaşık tür listeleriyle çalışıyorsanız, o kadar da hoş olmayan bir IEqualityComparer <MyComlplexType> uygulamanız gerekir
sakito

4
Sen yok olması sadece referans eşitlik karşılaştırmak istiyorsanız IEqualityComparer <T> uygulamak ya da geçersiz kılınmış T.Equals yasiyorsaniz () ve T.GetHashCode (). IEqualityComparer <T> uygulamıyorsanız, EqualityComparer <T> .Varsayılan kullanılır.
Piedar

2
@Echostorm (ve diğerleri okuyor), Anonim Olarak Seç nesnesi yaparsanız, HashCode özellik değerleri tarafından belirlenir; list1.Select(item => new { Property1 = item.Property1, Property2 = item.Property2 }).Except(list2.Select( item => new { Property1 = item.Property1, Property2 = item.Property2 }));bu, özellikle karmaşık türdeki bir dizi değeri değerlendirerek eşitliği belirlerken yararlıdır.
Brett Caswell

3
Aslında, birisi aşağıda işaret etti ve bence doğru, IEquatityComparor<T,T>bir LinqToSqlsenaryoda nesne karşılaştırma yöntemlerini uygulamaya veya geçersiz kılmaya gerek olmadığını ; için, sorgu / olarak derlenir / SQL olarak ifade edilir; böylece nesne referansı değil, değerler kontrol edilecektir.
Brett Caswell

2
exceptI kullanarak bir LINQ sorgusu 8-10 saniye yarım saniye hızlandırmak mümkün
Michael Kniskern

61

Bir grup bellek içi nesne ile başlayan ve bir veritabanını sorgulayan insanlar için, bu gitmek için en iyi yol olarak buldum:

var itemIds = inMemoryList.Select(x => x.Id).ToArray();
var otherObjects = context.ItemList.Where(x => !itemIds.Contains(x.Id));

Bu WHERE ... IN (...)SQL'de güzel bir cümle oluşturur.


1
aslında, bunu 3.5
George Silva

59

e-postanın ikinci listede bulunmadığı ilk listedeki öğeler.

from item1 in List1
where !(list2.Any(item2 => item2.Email == item1.Email))
select item1;

16

Bulunduğunuz yeri bulmak için Where ve Any kombinasyonunu kullanabilirsiniz:

var NotInRecord =list1.Where(p => !list2.Any(p2 => p2.Email  == p.Email));

8

Her iki koleksiyonu da list1 ve list2 gibi iki farklı listede alabilirsiniz.

Sonra yaz

list1.RemoveAll(Item => list2.Contains(Item));

Bu çalışacak.


3
Güzel ama listeden elemanları kaldırmanın yan etkisi var.
Tarik

7

Birinin ADO.NET Entity Framework'ü kullanması durumunda , EchoStorm'un çözümü de mükemmel şekilde çalışır. Ama başımı etrafına sarmak birkaç dakika sürdü. Bir veritabanı bağlamınız, dc olduğu ve tablo x'deki tablo y'de bağlı olmayan satırları bulmak istediğinizi varsayarsak, yanıtın tamamı şöyle görünür:

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);

Andy'nin yorumuna cevap olarak, evet, bir LINQ sorgusunda iki tane olabilir. Listeleri kullanarak tam bir çalışma örneği. Her sınıfın, Foo ve Bar'ın bir kimliği vardır. Foo'nun, Bar'a Foo.BarId üzerinden "yabancı anahtar" referansı vardır. Program karşılık gelen bir Çubuğa bağlı olmayan tüm Foo'ları seçer.

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; }
}

LINQ iki wors yapmak? bu yardımcı olur.
Andy

Andy: Evet, yukarıdaki revize cevaba bakınız.
Brett

4
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};

4

Bir de kullanabilirsiniz All()

var notInList = list1.Where(p => list2.All(p2 => p2.Email != p.Email));

2

ExceptCevabın bir parçası olsa da, yanıtın tamamı değil. Varsayılan olarak, Except(birçok LINQ operatörü gibi) referans türlerinde bir referans karşılaştırması yapar. Nesnelerdeki değerlerle karşılaştırmak için şunları yapmanız gerekir:

  • uygulamak IEquatable<T>türünüze veya
  • geçersiz kıl EqualsveGetHashCode veya
  • türünüz IEqualityComparer<T>için bir tür uygulamanın örneğini iletme

2
... LINQ to Objects hakkında konuşuyorsak. LINQ to SQL ise, sorgu veritabanında çalışan SQL deyimlerine çevrilir, bu nedenle bu geçerli değildir.
Lucas

1

Basitlik için int Listesi kullanılarak örnek.

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());

1

INC # ' da SQL benzeri bir operatör kullanmak isteyen herkes için şu paketi indirin:

Mshwf.NiceLinq

Bu sahiptir Inve NotInyöntem:

var result = list1.In(x => x.Email, list2.Select(z => z.Email));

Hatta bu şekilde kullanabilirsiniz

var result = list1.In(x => x.Email, "a@b.com", "b@c.com", "c@d.com");

0

Teşekkürler Brett. Önerin bana da yardımcı oldu. Nesnelerin bir listesi vardı ve başka bir nesne listesi kullanarak filtrelemek istedim. Tekrar teşekkürler....

Herkes gerekiyorsa, lütfen benim kod örneği bir göz atın:

'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

0

Bunu LINQ to Entities ile test etmedim :

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;

Alternatif:

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 );

0

Dış birleştirme yapamaz mısınız, sadece grup boşsa ilk listeden öğeleri seçer misiniz? Gibi bir şey:

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)

Bunun Varlık çerçevesi ile verimli bir şekilde çalışıp çalışmayacağından emin değilim.


0

Alternatif olarak bunu yapabilirsiniz:

var result = list1.Where(p => list2.All(x => x.Id != p.Id));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.