List <T> 'den her n'inci öğeyi nasıl alabilirim?


115

.NET 3.5 kullanıyorum ve her * n* inci öğeyi bir Listeden elde edebilmek istiyorum . Bunun lambda ifadesi veya LINQ kullanılarak elde edilip edilmediği konusunda endişelenmiyorum.

Düzenle

Görünüşe göre bu soru oldukça fazla tartışmaya neden oldu (bu iyi bir şey, değil mi?). Öğrendiğim en önemli şey, bir şeyi yapmanın her yolunu bildiğinizi düşündüğünüzde (bu kadar basit bile olsa), tekrar düşünün!


Orijinal sorunuzun arkasında herhangi bir anlam düzenlemedim; Yalnızca temizledim ve Büyük Harf ve noktalama işaretlerini doğru şekilde kullandım. (Büyük harfle .NET, LINQ hepsi Caps olduğunu ve bunun bir lamda, bir 'lambda ifadesi' olduğunu değil.)
George Stocker

1
Hiç eşanlamlı olmayan "telaşlı" ifadesini "emin" ile değiştirdiniz.
mqp

Öyle görünüyor. "... kullanarak başarılıp ulaşılamayacağından emin değilim" olmadığı sürece emin olmak da mantıklı değil
Samuel

Evet, anladığım kadarıyla bu doğru.
mqp

karışıklık, muhtemelen "endişeli" ile değiştirilmesi en iyisidir, böylece "Lambda ifadesi veya LINQ kullanılarak elde edilip edilmediği konusunda endişelenmiyorum."
TheTXI

Yanıtlar:


191
return list.Where((x, i) => i % nStep == 0);

5
@mquander: Bunun size n'inci - 1 elementini vereceğini unutmayın. Eğer gerçek n'inci elemanları istiyorsanız (ilkini atlayarak), o zaman 1'den i'ye eklemeniz gerekecektir.
casperOne

2
Evet, sanırım bu "nth" ile neyi kastettiğinize bağlı, ancak yorumunuz daha yaygın olabilir. İhtiyaçlarınıza uyacak şekilde i ekleyin veya çıkarın.
mqp

5
Sadece not etmek gerekirse: Linq / Lambda çözümü, sabit artışlı basit bir döngüden çok daha az performanslı olacaktır.
MartinStettner

5
Zorunlu değil, ertelenmiş yürütme ile bir foreach döngüsünde kullanılabilir ve orijinal listenin üzerinde yalnızca bir kez döngüye girer.
Samuel

1
"Pratik" ile ne demek istediğine bağlı. Kullanıcı bir düğmeyi tıkladığında diğer her öğeyi 30 öğelik bir listede almanın hızlı bir yoluna ihtiyacınız varsa, bunun her parçasının pratik olduğunu söyleyebilirim. Bazen performans artık gerçekten önemli değil. Elbette bazen oluyor.
mqp

37

Bunun "eski usul" olduğunu biliyorum, ama neden adımlama = n olan bir for döngüsü kullanmıyorsunuz?


Bu temelde benim düşüncemdi.
Mark Pim

1
@Michael Todd: İşe yarıyor, ancak bununla ilgili sorun, bu işlevi her yerde kopyalamak zorunda olmanız. LINQ kullanarak, oluşturulan sorgunun bir parçası haline gelir.
casperOne

8
@casperOne: Programcıların bununla başa çıkmak için alt yordamlar denen şeyi icat ettiğine inanıyorum;) Gerçek bir programda, akıllı LINQ sürümüne rağmen muhtemelen bir döngü kullanırım çünkü bir döngü, her öğeyi yinelemeniz gerekmediği anlamına gelir ( dizini N
artır

Eski okul çözümünü kabul ediyorum ve bunun daha iyi performans göstereceğini tahmin ediyorum.
Jesper Fyhr Knudsen

Süslü yeni sözdizimi ile kendinizi kaptırmak kolaydır. Yine de eğlenceli.
Ronnie

34

Gibi geliyor

IEnumerator<T> GetNth<T>(List<T> list, int n) {
  for (int i=0; i<list.Count; i+=n)
    yield return list[i]
}

hile yapardı. Linq veya lambda ifadeleri kullanma gereğini görmüyorum.

DÜZENLE:

Bunu yapmak

public static class MyListExtensions {
  public static IEnumerable<T> GetNth<T>(this List<T> list, int n) {
    for (int i=0; i<list.Count; i+=n)
      yield return list[i];
  }
}

ve LINQish tarzında yazıyorsun

from var element in MyList.GetNth(10) select element;

2. Düzenleme :

Daha da fazla LINQish yapmak için

from var i in Range(0, ((myList.Length-1)/n)+1) select list[n*i];

2
Esasen IEnumerable'ın her öğesini yineleyen Where () yöntemi yerine bu [] alıcıyı kullanmak için bu yöntemi seviyorum. Bir IList / ICollection türüne sahipseniz, bu daha iyi bir yaklaşımdır, IMHO.
spoulson

Listenin nasıl çalıştığından emin değilsiniz, ancak neden bir döngü kullanıp list[i]bunun yerine geri dönüyorsunuz list[n-1]?
Juan Carlos Oropeza

@JuanCarlosOropeza, sadece listenin n'inci elemanını değil, her n'inci elemanı (örneğin 0, 3, 6 ...) döndürür.
alfoks

27

Dizini elemanla birlikte geçiren Nerede aşırı yükünü kullanabilirsiniz.

var everyFourth = list.Where((x,i) => i % 4 == 0);

1
Bu yöntemin hayranı olduğumu söylemeliyim.
Quintin Robinson

1
Bunu yapabileceğini unutuyorum - çok güzel.
Stephen Newman

10

Döngü için

for(int i = 0; i < list.Count; i += n)
    //Nth Item..

Count, numaralandırılabilirliği değerlendirecektir. eğer bu çok kolay bir şekilde yapıldıysa, tembelce değerlendirebilir ve ilk 100 değeri alabilirsin, örneğin `` source.TakeEvery (5). (100) '' Al değerlendirilecek öğe
RhysC

1
@RhysC Genel olarak numaralar için iyi bir nokta. OTOH, Soru belirtti List<T>, bu yüzden Countucuz olarak tanımlandı.
ToolmakerSteve

3

Bir LINQ ifadesiyle yapmanın mümkün olup olmadığından emin değilim, ancak bunu Whereyapmak için uzantı yöntemini kullanabileceğinizi biliyorum. Örneğin, her beşinci öğeyi almak için:

List<T> list = originalList.Where((t,i) => (i % 5) == 0).ToList();

Bu, ilk öğeyi ve oradan her beşte birini alacak. Birincisi yerine beşinci maddeden başlamak istiyorsanız, 0 ile karşılaştırmak yerine 4 ile karşılaştırırsınız.


3

Bence bir linq uzantısı sağlarsanız, en az spesifik arayüzde, dolayısıyla IEnumerable'da çalışabilmelisiniz. Elbette, özellikle büyük N için hızınız varsa, indekslenmiş erişim için aşırı yük sağlayabilirsiniz. İkincisi, büyük miktarlarda ihtiyaç duyulmayan veriyi yineleme ihtiyacını ortadan kaldırır ve Where cümlesinden çok daha hızlı olacaktır. Her iki aşırı yüklemeyi de sağlamak, derleyicinin en uygun değişkeni seçmesini sağlar.

public static class LinqExtensions
{
    public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n)
    {
        if (n < 0)
            throw new ArgumentOutOfRangeException("n");
        if (n > 0)
        {
            int c = 0;
            foreach (var e in list)
            {
                if (c % n == 0)
                    yield return e;
                c++;
            }
        }
    }
    public static IEnumerable<T> GetNth<T>(this IList<T> list, int n)
    {
        if (n < 0)
            throw new ArgumentOutOfRangeException("n");
        if (n > 0)
            for (int c = 0; c < list.Count; c += n)
                yield return list[c];
    }
}

Bu herhangi bir Liste için çalışıyor mu? çünkü bir List içinde özel bir sınıf için kullanmayı deniyorum ve <class> yerine IEnumarted <class> döndürüyorum ve coversion (class) List.GetNth (1) zorlama da çalışmıyor.
Juan Carlos Oropeza

Benim hatamdı GetNth (1) .FirstOrDefault ();
Juan Carlos Oropeza

0
private static readonly string[] sequence = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15".Split(',');

static void Main(string[] args)
{
    var every4thElement = sequence
      .Where((p, index) => index % 4 == 0);

    foreach (string p in every4thElement)
    {
        Console.WriteLine("{0}", p);
    }

    Console.ReadKey();
}

çıktı

görüntü açıklamasını buraya girin


0

Hiçbir cevap doğru değil. Tüm çözümler 0'dan başlar. Ama gerçek n'inci öğeye sahip olmak istiyorum

public static IEnumerable<T> GetNth<T>(this IList<T> list, int n)
{
    for (int i = n - 1; i < list.Count; i += n)
        yield return list[i];
}

0

@belucha Bunu beğendim, çünkü istemci kodu çok okunabilir ve Derleyici en verimli Uygulamayı seçiyor. Bunun üzerine, gereksinimleri azaltarak IReadOnlyList<T>ve Bölümü yüksek performanslı LINQ için kaydederek inşa edeceğim :

    public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n) {
        if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
        int i = n;
        foreach (var e in list) {
            if (++i < n) { //save Division
                continue;
            }
            i = 0;
            yield return e;
        }
    }

    public static IEnumerable<T> GetNth<T>(this IReadOnlyList<T> list, int n
        , int offset = 0) { //use IReadOnlyList<T>
        if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
        for (var i = offset; i < list.Count; i += n) {
            yield return list[i];
        }
    }
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.