2011-09-15 18 views
8
n listeleri karşılaştırmak ve tüm listelerde görünmüyor tüm değerlerini geri getirecek bir yöntem yazmak en etkili yolu, nedir

oLinq olsun değerler birden fazla liste arasında paylaşılmayan

var lists = new List<List<int>> { 
            new List<int> { 1, 2, 3, 4 }, 
            new List<int> { 2, 3, 4, 5, 8 }, 
            new List<int> { 2, 3, 4, 5, 9, 9 }, 
            new List<int> { 2, 3, 3, 4, 9, 10 } 
           }; 


public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) 
{ 
    //...fast algorithm here 
} 
böylece

o

böylece

lists.GetNonShared();

döner 1, 5, 8, 9, 10

Ben

public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) 
{ 
    return list.SelectMany(item => item) 
      .Except(lists.Aggregate((a, b) => a.Intersect(b)); 
} 

vardı Ama bu verimli olsaydı ben emin değildi. Sipariş önemli değil. Teşekkürler!

+1

için HashSet ve SymmetricExceptWith kullanabilir? Sorun bu değil. Sorun şu: doğru olan semantik nedir ve performans gereksinimlerinizi karşılar mı? Uygulamanızın semantiği doğrudur. Performans gereksinimlerinizi karşılayıp karşılamadığını yalnızca siz bilirsiniz. – jason

cevap

5
 public static IEnumerable<T> GetNonShared<T>(this IEnumerable<IEnumerable<T>> list) 
     { 
      return list.SelectMany(x => x.Distinct()).GroupBy(x => x).Where(g => g.Count() < list.Count()).Select(group => group.Key); 
     } 
+0

Sağlanan listelerin birinden bir tanesini 9 ekleyin ve bu başarısız olur. –

+0

.Distinct() – mironych

2

DÜZENLEME:

Tüm listelerin birliği istiyorum ... ben böyle düşünmek düşünüyorum, eksi tüm listelerin kesişim. Bu, orijinalinizin ne yaptığını etkili bir şekilde, Except'u kopyalarken, yinelenen girişlere rağmen "set" işlemini Union yapıyor. Bu durumda ben şüpheli sadece iki HashSet s birikmesini ve yerinde tüm işi daha verimli yapabilirsiniz: Bu şimdi istekli olduğunu

public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) 
{   
    using (var iterator = lists.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      return new T[0]; // Empty 
     } 

     HashSet<T> union = new HashSet<T>(iterator.Current.ToList()); 
     HashSet<T> intersection = new HashSet<T>(union); 
     while (iterator.MoveNext()) 
     { 
      // This avoids iterating over it twice; it may not be necessary, 
      // it depends on how you use it. 
      List<T> list = iterator.Current.Toist(); 
      union.UnionWith(list); 
      intersection = intersection.IntersectWith(list); 
     } 
     union.ExceptWith(intersection); 
     return union; 
    } 
} 

Not, ertelenmiş değil.

bir liste birden fazla kez aynı madde daha içermesi mümkün değilse
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) 
{ 
    return list.SelectMany(list => list) 
       .GroupBy(x => x) 
       .Where(group => group.Count() < lists.Count) 
       .Select(group => group.Key); 
} 

, orada Bir Farklı çağrıyı isterdim:

public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) 
{ 
    return list.SelectMany(list => list.Distinct()) 
       .GroupBy(x => x) 
       .Where(group => group.Count() < list.Count) 
       .Select(group => group.Key); 
} 


İşte alternatif bir seçenek

DÜZENLEME: Şimdi bunu düzelttim, orijinal kodunuzu anlıyorum ... ve sanırım daha iyi bir şey bulabileceğime dair şüphelerim var ... düşünmek ...

+1

Bu 5 & 9'u hariç tutar. Yalnızca tüm listelerde yer alan tüm değerler için ortak olan değerleri istiyor. –

+0

@Austin: Ah, yanlış okuyor. Kolay düzeltme olsa da :) –

+0

Tüm listeleri düzleştirir ve tüm listelerde bulunan öğeleri (toplama işlemi tarafından hesaplanan) kaldırır. – jason

0

tüm listelerde ortak olan tüm öğeleri bulacağınız bir ara adım oluşturmanız gerektiğini düşünüyorum. Bu, set mantığı ile çok kolaydır - sadece, her bir sonraki listede yer alan öğeler kümesiyle kesişen ilk liste içindeki öğeler kümesidir. Yine de bu adımın LINQ'da yapılabileceğini sanmıyorum.

class Program 
{ 
    static void Main(string[] args) 
    { 
     IEnumerable<IEnumerable<int>> lists = new List<IEnumerable<int>> { 
           new List<int> { 1, 2, 3, 4 }, 
           new List<int> { 2, 3, 4, 5, 8 }, 
           new List<int> { 2, 3, 4, 5, 9, 9 }, 
           new List<int> { 2, 3, 3, 4, 9, 10 } 
          }; 

     Console.WriteLine(string.Join(", ", GetNonShared(lists) 
      .Distinct() 
      .OrderBy(x => x) 
      .Select(x => x.ToString()) 
      .ToArray())); 
     Console.ReadKey(); 
    } 

    public static HashSet<T> GetShared<T>(IEnumerable<IEnumerable<T>> lists) 
    { 
     HashSet<T> result = null; 
     foreach (IEnumerable<T> list in lists) 
     { 
      result = (result == null) 
         ? new HashSet<T>(list) 
         : new HashSet<T>(result.Intersect(list)); 
     } 
     return result; 
    } 

    public static IEnumerable<T> GetNonShared<T>(IEnumerable<IEnumerable<T>> lists) 
    { 
     HashSet<T> shared = GetShared(lists); 
     return lists.SelectMany(x => x).Where(x => !shared.Contains(x)); 
    } 
} 
0
public static IEnumerable<T> GetNonShared<T>(this IEnumerable<IEnumerable<T>> list) 
{ 
    var lstCnt=list.Count(); //get the total number if items in the list         
    return list.SelectMany (l => l.Distinct()) 
     .GroupBy (l => l) 
     .Select (l => new{n=l.Key, c=l.Count()}) 
     .Where (l => l.c<lstCnt) 
     .Select (l => l.n) 
     .OrderBy (l => l) //can be commented 
     ; 
} 

// Bunu "verimli" olmadığından emin değiliz .net> = 4.5

+0

ile düzeltildi; bu, cevabınızı açıklamak için daha yararlıdır. –

+1

temel olarak, her bir listeden tüm ayrı öğeleri düzleştirilmiş bir çıkış (selectMany) listesine alır ve her bir öğenin değeri, kaç kez (l.c) (l.n) oluştuğunu (düzleştirilmiş listede) elde etmek için değeriyle gruplandırır. Eğer l.c (herhangi bir öğe için) toplam listenin (lstCnt) toplam sayısından daha az ise, öğenin en az bir listede bulunmadığından emin olabiliriz. – SKG

İlgili konular