Bagaimana saya bisa mendapatkan setiap item n dari Daftar <T>?

114

Saya menggunakan .NET 3.5 dan ingin mendapatkan setiap * n* item dari Daftar. Saya tidak peduli apakah itu dicapai dengan menggunakan ekspresi lambda atau LINQ.

Sunting

Sepertinya pertanyaan ini memancing cukup banyak perdebatan (mana hal yang bagus, bukan?). Hal utama yang saya pelajari adalah ketika Anda berpikir Anda tahu segala cara untuk melakukan sesuatu (bahkan sesederhana ini), pikirkan lagi!

Paul Suart
sumber
Saya tidak menyunting makna apa pun di balik pertanyaan awal Anda; Saya hanya membersihkannya dan menggunakan Kapitalisasi dan tanda baca dengan benar. (.NET dikapitalisasi, LINQ menggunakan huruf kapital semua, dan ini bukan 'lambda', ini adalah 'ekspresi lambda'.)
George Stocker
1
Anda mengganti "fussed" dengan "sure" yang sama sekali bukan sinonim.
mqp
Sepertinya begitu. Memiliki kepastian juga tidak masuk akal, kecuali jika "Saya tidak yakin apakah itu dapat dicapai dengan menggunakan ..."
Samuel
Ya, seperti yang saya pahami, itu benar.
mqp
rewel mungkin lebih baik diganti dengan "prihatin" sehingga berbunyi "Saya tidak peduli apakah itu dicapai dengan menggunakan ekspresi lambda atau LINQ."
TheTXI

Jawaban:

189
return list.Where((x, i) => i % nStep == 0);
Mqp
sumber
5
@mquander: Perhatikan bahwa ini sebenarnya akan memberi Anda elemen nth - 1. Jika Anda menginginkan elemen ke-n yang sebenarnya (melompati yang pertama) maka Anda harus menambahkan 1 ke i.
casperOne
2
Ya, saya kira itu tergantung pada apa yang Anda maksud dengan "n," tetapi interpretasi Anda mungkin lebih umum. Tambah atau kurangi dari i untuk menyesuaikan dengan kebutuhan Anda.
mqp
5
Sekadar catatan: Solusi Linq / Lambda akan memiliki kinerja yang jauh lebih rendah daripada loop sederhana dengan kenaikan tetap.
MartinStettner
5
Belum tentu, dengan eksekusi yang ditangguhkan itu bisa digunakan dalam loop foreach dan hanya mengulang daftar asli sekali.
Samuel
1
Tergantung apa yang Anda maksud dengan "praktis". Jika Anda memerlukan cara cepat untuk mendapatkan setiap item lain dalam daftar 30 item saat pengguna mengklik tombol, menurut saya ini sama praktisnya. Terkadang kinerja benar-benar tidak penting lagi. Tentu saja, terkadang demikian.
mqp
37

Saya tahu ini "jadul", tetapi mengapa tidak menggunakan loop for dengan stepping = n?

Michael Todd
sumber
Itu pada dasarnya adalah pikiranku.
Mark Pim
1
@Michael Todd: Ini berfungsi, tetapi masalahnya adalah Anda harus menggandakan fungsionalitas itu di mana-mana. Dengan menggunakan LINQ, ini menjadi bagian dari kueri yang dibuat.
casperOne
8
@casperOne: Saya percaya programmer menemukan hal yang disebut subrutin ini untuk menangani ini;) Dalam program nyata saya mungkin akan menggunakan loop, terlepas dari versi LINQ yang pintar, karena loop berarti Anda tidak perlu mengulangi setiap elemen (
tingkatkan
Saya setuju untuk menggunakan solusi jadul, dan saya bahkan menduga ini akan bekerja lebih baik.
Jesper Fyhr Knudsen
Mudah terbawa suasana dengan sintaks baru yang mewah. Ini menyenangkan.
Ronnie
34

Terdengar seperti

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

akan berhasil. Saya tidak melihat perlunya menggunakan LINQ atau ekspresi lambda.

EDIT:

Lakukan

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

dan Anda menulis dengan cara LINQish

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

Edit kedua :

Untuk membuatnya lebih LINQish

from var i in Range(0, ((myList.Length-1)/n)+1) select list[n*i];
MartinStettner
sumber
2
Saya suka metode ini untuk menggunakan metode [] getter ini, bukan Where (), yang pada dasarnya mengulang setiap elemen IEnumerable. Jika Anda memiliki tipe IList / ICollection, ini adalah pendekatan yang lebih baik, IMHO.
spoulson
Tidak yakin bagaimana daftar bekerja, tetapi mengapa Anda menggunakan perulangan dan kembali list[i]sebagai gantinya hanya kembali list[n-1]?
Juan Carlos Oropeza
@JuanCarlosOropeza dia mengembalikan setiap elemen ke-n (misalnya 0, 3, 6 ...), bukan hanya elemen ke-n dari daftar.
alfoks
27

Anda dapat menggunakan Where overload yang meneruskan indeks bersama dengan elemennya

var everyFourth = list.Where((x,i) => i % 4 == 0);
JaredPar
sumber
1
Harus dikatakan saya penggemar metode ini.
Quintin Robinson
1
Saya selalu lupa Anda bisa melakukan itu - sangat bagus.
Stephen Newman
10

Untuk Loop

for(int i = 0; i < list.Count; i += n)
    //Nth Item..
Quintin Robinson
sumber
Hitungan akan mengevaluasi yang dapat dihitung. jika ini dilakukan dengan cara yang ramah linq maka Anda dapat dengan malas mengevaluasi dan mengambil 100 nilai pertama misalnya `` source.TakeEvery (5) .Take (100) `` Jika sumber yang mendasari mahal untuk dievaluasi maka pendekatan Anda akan menyebabkan setiap elemen yang akan dievaluasi
RhysC
1
@RhysC Poin yang bagus, untuk enumerable secara umum. OTOH, Pertanyaan memang menentukan List<T>, jadi Countdiartikan sebagai murah.
ToolmakerSteve
3

Saya tidak yakin apakah itu mungkin dilakukan dengan ekspresi LINQ, tetapi saya tahu bahwa Anda dapat menggunakan Wheremetode ekstensi untuk melakukannya. Misalnya untuk mendapatkan setiap item kelima:

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

Ini akan mendapatkan item pertama dan setiap kelima dari sana. Jika Anda ingin memulai dari item kelima dan bukan yang pertama, Anda membandingkan dengan 4 daripada membandingkan dengan 0.

Guffa
sumber
3

Saya rasa jika Anda menyediakan ekstensi LINQ, Anda harus dapat beroperasi pada antarmuka yang paling tidak spesifik, sehingga pada IEnumerable. Tentu saja, jika Anda siap untuk kecepatan terutama untuk N besar Anda mungkin memberikan kelebihan beban untuk akses yang diindeks. Yang terakhir menghilangkan kebutuhan untuk mengulang sejumlah besar data yang tidak diperlukan, dan akan jauh lebih cepat daripada klausa Where. Menyediakan kedua kelebihan beban memungkinkan kompilator memilih varian yang paling sesuai.

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];
    }
}
belucha
sumber
Ini berfungsi untuk Daftar apa pun? karena saya mencoba menggunakan dalam Daftar untuk kelas khusus dan mengembalikan <class> IEnumarted bukan <class> dan memaksa coverion (class) List.GetNth (1) tidak berfungsi juga.
Juan Carlos Oropeza
Apakah salah saya, saya harus menyertakan 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();
}

keluaran

masukkan deskripsi gambar di sini

Anwar Ul Haq
sumber
0

Imho tidak ada jawaban yang benar. Semua solusi dimulai dari 0. Tapi saya ingin memiliki elemen ke-n yang sebenarnya

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];
}
pengguna2340145
sumber
0

@belucha Saya suka ini, karena kode klien sangat mudah dibaca dan Compiler memilih Implementasi yang paling efisien. Saya akan membangun ini dengan mengurangi persyaratan untuk IReadOnlyList<T>dan menyimpan Divisi untuk LINQ berkinerja tinggi:

    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];
        }
    }
Spoc
sumber