LINQ

2012-06-19 39 views
5
kullanarak sonraki kullanılabilir tamsayı olsun

Ben tamsayılar listesi var ki: Ben bir sonraki kullanılabilir tamsayı almak istiyorumLINQ

List<int> myInts = new List<int>() {1,2,3,5,8,13,21}; 

, tamsayı artırarak emretti. Son veya en yüksek olanı değil, bu durumda bu listede olmayan sonraki tamsayı. Bu durumda, sayı 4'dir.

Bana bunu verecek bir LINQ ifadesi var mı? Olarak:

var nextAvailable = myInts.SomeCoolLinqMethod(); 

Düzenleme: Crap. Cevabın 2 olması gerektiğini söyledik ama ben 4 demek istedim. Bunun için özür dilerim! Örneğin, süreç kimliklerini teslim etmekle sorumlu olduğunuzu düşünün. Mevcut işlem kimlikleri listesini almak ve bir sonrakini vermek istiyorsunuz, ancak bir sonraki en yüksek değer artı bir değil. Aksine, sıralı bir süreç ID'leri listesinden mevcut olan bir sonraki olmalıdır. En yüksek ile başlayan bir sonraki elde edebilirsiniz, gerçekten önemli değil.

+2

'Sonraki kullanılabilir'. Ne ile ilgili? – Chandu

+0

Sanırım düzenli bir liste arıyor –

+0

Artan tamsayılarla ilgili sonraki. Yani evet, bu sıralı bir liste için, tamsayısal karşılaştırma önemlidir. Listeyi sipariş etmek için –

cevap

0
public static class IntExtensions 
{ 
    public static int? SomeCoolLinqMethod(this IEnumerable<int> ints) 
    { 
     int counter = ints.Count() > 0 ? ints.First() : -1; 

     while (counter < int.MaxValue) 
     { 
      if (!ints.Contains(++counter)) return counter; 
     } 

     return null; 
    } 
} 

Kullanımı:

var nextAvailable = myInts.SomeCoolLinqMethod(); 
+0

olacaktır. Bu size temel int uzantıları sınıfımda uygulamış olduğum için teşekkür ederiz. Bunu yapacak bir şey olduğu konusunda emin değildim, ama bu yöntemle en azından yerel olarak bir tane var. –

+0

Bu yöntemdeki en büyük sorun, ints’in önemsiz veya bir veritabanından gelmesi durumunda verimliliği yetersiz olmasıdır. – user7116

+1

Ama çözmeye çalıştığım sorun bu değil. Optimizasyon, ana mantık gerçekleştikten ve sadece İF'ye ihtiyaç duyulana kadar en iyi durumda kalır. –

0

Tamam, burada o benim için çalışıyor ile ben geldi çözümdür.

var nextAvailableInteger = Enumerable.Range(myInts.Min(),myInts.Max()).FirstOrDefault(r=> !myInts.Contains(r)); 

Eğer daha zarif bir çözümü varsa, bunu kabul etmekten mutluluk duyarım. Ama şimdilik, bu benim koduma koyduğum ve devam ettiğim şey.

Düzenleme: Bu, Kevin'ın bir uzantı yöntemi ekleme önerisinden sonra uyguladığım şeydir. Ve bu gerçek bir cevaptı - hiçbir LINQ uzantısının yapamayacağından, kendi başıma eklemek daha mantıklıydı. Gerçekten aradığım şey bu.

public static int NextAvailableInteger(this IEnumerable<int> ints) 
{ 
    return NextAvailableInteger(ints, 1); // by default we use one 
} 
public static int NextAvailableInteger(this IEnumerable<int> ints, int defaultValue) 
{ 
    if (ints == null || ints.Count() == 0) return defaultValue; 

    var ordered = ints.OrderBy(v => v); 
    int counter = ints.Min(); 
    int max = ints.Max(); 

    while (counter < max) 
    { 
     if (!ordered.Contains(++counter)) return counter; 
    } 
    return (++counter); 
} 
+0

LINQ'dan Nesnelere kullanıyorsanız, hiçbir şey fark etmeyebilirsiniz. LINQ-to-SQL kullanıyorsanız, bu gerçekten çok kötü bir performans sergileyecektir. Tüm numaralandırmayı geçmeyi gerektiren birden fazla LINQ işlevini çağırıyorsunuz! Ayrıca, sıralı listeye zaten sahipsiniz, bu yüzden min ilk ve en sonuncusu. – user7116

+0

Bu sadece Linq'den Objects içindir. Yine de teşekkürler. –

2

Bu oldukça etkili olacaktır: @Kevin sağladığı

static int Next(this IEnumerable<int> source) 
{ 
    int? last = null; 
    foreach (var next in source.OrderBy(_ => _)) 
    { 
     if (last.HasValue && last.Value + 1 != next) 
     { 
      return last.Value + 1; 
     } 

     last = next; 
    } 

    return last.HasValue ? last.Value + 1 : Int32.MaxValue; 
} 
3

cevap istenmeyen bir performans profiline sahiptir. Mantık, kaynak dizisine birçok kez erişecektir: bir kez .Count çağrısı için, bir kez .FirstOrDefault çağrısı için ve her bir .Contains çağrısı için bir kez. IEnumerable<int> örneği, bir .Select çağrısının sonucu gibi ertelenmiş bir sıra ise, bu, her sayı için bir kez ile birlikte, dizinin en az 2 hesaplamasına neden olur. Eğer yönteme listesini geçmesi bile, potansiyel olarak her kontrol numarası için tüm liste üzerinden gidecek. Bunu { 1, 1000000 } dizisinde çalıştırdığınızı ve bunun nasıl iyi performans göstermeyeceğini görebilirsiniz. LINQ, kaynak dizilerini bir kereden fazla tekrar etmeyi amaçlamaktadır. Bu genel olarak mümkündür ve kodunuzun performansı üzerinde büyük bir etkisi olabilir. Aşağıda diziyi tam olarak bir kez yineleyecek bir uzantı yöntemidir.

public static int? FirstMissing(this IEnumerable<int> numbers) 
{ 
    int? priorNumber = null; 

    foreach(var number in numbers.OrderBy(n => n)) 
    { 
     var difference = number - priorNumber; 

     if(difference != null && difference > 1) 
     { 
      return priorNumber + 1; 
     } 

     priorNumber = number; 
    } 

    return priorNumber == null ? (int?) null : priorNumber + 1; 
} 

Bu uzantı metodu herhangi bir keyfi sekansı çağrılabilir Süresi: bu yüzden her bir ardışık çifti arasındaki farkı bakarak, daha sonra en fazla 1 uzak bir sonraki numaradan olan birinci alt sayısı 1 ekler etmez Tamsayılardan önce, bunları tekrarlamadan önce sipariş verdiğimizden emin oluruz. Daha sonra mevcut sayı ile önceki sayı arasındaki farkı hesaplıyoruz.Bu listedeki ilk sayıysa, priorNumber boş olacaktır ve bu nedenle difference boş olacaktır. Listedeki ilk sayı bu değilse, önceki numaradan farkın tam olarak 1 olup olmadığını kontrol ederiz. Değilse, bir boşluk olduğunu biliyoruz ve önceki sayıya 1 ekleyebiliriz.

Sıralama ifadesini, uygun gördüğünüz gibi 0 veya 1 öğe içeren sekmeleri işlemek için ayarlayabilirsiniz; Boş diziler için null ve dizisi için n + 1 döndürmeyi seçtim.

23

Özel bir uzantısı yöntemi yazma cevapları bir sürü görmek, ama standart linq uzantısı yöntemleri ve statik Enumerable sınıfıyla bu sorunu çözmek mümkündür: Bu şekilde bu kapsamda olup olmadığını

List<int> myInts = new List<int>() {1,2,3,5,8,13,21}; 

// This will set firstAvailable to 4. 
int firstAvailable = Enumerable.Range(1, Int32.MaxValue).Except(myInts).First(); 
+0

Daha iyi bir performansa sahip, daha önce verilmiş bir cevap! – Rekshino

+1

bir yazım hatası buldu, ancak daha az karakterin nedenini değiştiremedi ;-) avaiable => available – Mat

0

emin değil serin Linq yöntemi ama sol dış kullanarak This SO Answer

var thelist = new List<int> {1,2,3,4,5,100,101}; 

var nextAvailable = (from curr in thelist 
        join next in thelist 
         on curr + 1 equals next into g 
        from newlist in g.DefaultIfEmpty() 
        where !g.Any() 
        orderby curr 
        select curr + 1).First(); 

Bu, size LINQ to SQL kullanıyorsanız sql sunucu tarafında işleme koyar fikri katılmak ve kimlik listeleri çekmek zorunda değilsiniz sağlar sunucudan memora ry.