Katakanlah saya memiliki urutan.
IEnumerable<int> sequence = GetSequenceFromExpensiveSource();
// sequence now contains: 0,1,2,3,...,999999,1000000
Mendapatkan urutan tidak murah dan dihasilkan secara dinamis, dan saya ingin mengulanginya sekali saja.
Saya ingin mendapatkan 0 - 999999 (yaitu semuanya kecuali elemen terakhir)
Saya menyadari bahwa saya dapat melakukan sesuatu seperti:
sequence.Take(sequence.Count() - 1);
tetapi itu menghasilkan dua enumerasi dalam urutan besar.
Apakah ada konstruksi LINQ yang memungkinkan saya melakukannya:
sequence.TakeAllButTheLastElement();
sequenceList.RemoveAt(sequence.Count - 1);
. Dalam kasus saya ini dapat diterima karena bagaimanapun juga manipulasi LINQ saya harus mengubahnya menjadi array atauIReadOnlyCollection
tetap. Saya ingin tahu apa kasus penggunaan Anda di mana Anda bahkan tidak mempertimbangkan caching? Seperti yang saya lihat, bahkan jawaban yang disetujui melakukan caching sehingga konversi yang sederhana menjadiList
lebih mudah dan lebih pendek menurut saya.Jawaban:
Saya tidak tahu solusi Linq - Tapi Anda dapat dengan mudah kode algoritma sendiri menggunakan generator (hasil pengembalian).
Atau sebagai solusi umum membuang n item terakhir (menggunakan antrian seperti yang disarankan dalam komentar):
sumber
Sebagai alternatif untuk membuat metode Anda sendiri dan dalam kasus urutan elemen tidak penting, selanjutnya akan berfungsi:
sumber
equence.Reverse().Skip(1).Reverse()
bukan solusi yang baikKarena saya bukan penggemar penggunaan eksplisit
Enumerator
, inilah alternatifnya. Perhatikan bahwa metode pembungkus diperlukan untuk membiarkan argumen yang tidak valid membuang lebih awal, daripada menunda pemeriksaan sampai urutan sebenarnya disebutkan.Sesuai saran Eric Lippert, ini dengan mudah digeneralisasikan ke n item:
Di mana saya sekarang buffer sebelum menghasilkan bukan setelah menghasilkan, sehingga
n == 0
kasing tidak perlu penanganan khusus.sumber
buffered=false
klausa lain sebelum menetapkanbuffer
. Kondisi ini sudah diperiksa, tetapi ini akan menghindari pengaturan yang berlebihanbuffered
setiap kali melalui loop.DropLast
. Kalau tidak, validasi hanya terjadi ketika Anda benar-benar menghitung urutan (pada panggilan pertama keMoveNext
hasilIEnumerator
). Bukan hal yang menyenangkan untuk di-debug ketika mungkin ada jumlah kode yang berubah-ubah antara membuatIEnumerable
dan benar-benar menghitungnya. Saat ini saya akan menulisInternalDropLast
sebagai fungsi dalamDropLast
, tetapi fungsi itu tidak ada di C # ketika saya menulis kode ini 9 tahun yang lalu.The
Enumerable.SkipLast(IEnumerable<TSource>, Int32)
Metode ditambahkan dalam NET Standard 2.1. Itu tepat seperti yang Anda inginkan.Dari https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast
sumber
Tidak ada dalam BCL (atau MoreLinq saya percaya), tetapi Anda dapat membuat metode ekstensi Anda sendiri.
sumber
if (!first)
danfirst = false
keluar dari if.prev
dalam iterasi pertama, dan mengulang selamanya setelah itu'.using
pernyataan sekarang.Akan sangat membantu jika .NET Framework dikirimkan dengan metode ekstensi seperti ini.
sumber
Sedikit ekspansi pada solusi Joren yang elegan:
Di mana shrink mengimplementasikan penghitungan sederhana ke depan untuk menjatuhkan
left
banyak elemen pertama dan buffer buangan yang sama untuk menjatuhkanright
banyak elemen terakhir .sumber
jika Anda tidak punya waktu untuk meluncurkan ekstensi Anda sendiri, inilah cara yang lebih cepat:
sumber
Sedikit variasi pada jawaban yang diterima, yang (sesuai selera saya) sedikit lebih sederhana:
sumber
Jika Anda bisa mendapatkan
Count
atauLength
menghitung, yang dalam banyak kasus Anda bisa, maka adilTake(n - 1)
Contoh dengan array
Contoh dengan
IEnumerable<T>
sumber
Mengapa tidak hanya
.ToList<type>()
pada urutan, lalu panggil hitungan dan ambil seperti yang Anda lakukan pada awalnya..tapi karena sudah ditarik ke dalam daftar, seharusnya tidak melakukan penghitungan mahal dua kali. Baik?sumber
Solusi yang saya gunakan untuk masalah ini sedikit lebih rumit.
Kelas statis util saya berisi metode ekstensi
MarkEnd
yang mengubahT
-items diEndMarkedItem<T>
-items. Setiap elemen ditandai dengan tambahanint
, yaitu 0 ; atau (dalam kasus satu sangat tertarik pada 3 item terakhir) -3 , -2 , atau -1 untuk 3 item terakhir.Ini bisa berguna sendiri, misalnya ketika Anda ingin membuat daftar di
foreach
-loop sederhana dengan koma setelah setiap elemen kecuali 2 terakhir, dengan item kedua-terakhir diikuti oleh kata penghubung (seperti " dan " atau " atau "), dan elemen terakhir diikuti oleh titik.Untuk menghasilkan seluruh daftar tanpa n item terakhir , metode ekstensi
ButLast
hanya mengulang selama beberapaEndMarkedItem<T>
saatEndMark == 0
.Jika Anda tidak menentukan
tailLength
, hanya item terakhir yang ditandai (masukMarkEnd()
) atau dijatuhkan (masukButLast()
).Seperti solusi lain, ini bekerja dengan buffering.
sumber
sumber
Saya tidak berpikir itu bisa menjadi lebih ringkas dari ini - juga memastikan untuk Buang
IEnumerator<T>
:Sunting: secara teknis identik dengan jawaban ini .
sumber
Dengan C # 8.0 Anda dapat menggunakan Rentang dan indeks untuk itu.
Secara default C # 8.0 memerlukan .NET Core 3.0 atau .NET Standard 2.1 (atau lebih tinggi). Periksa utas ini untuk digunakan dengan implementasi yang lebih lama.
sumber
Anda bisa menulis:
sumber
Ini adalah solusi umum dan IMHO elegan yang akan menangani semua kasus dengan benar:
sumber
IEnumerable
Pendekatan tradisional saya :sumber
Cara sederhana adalah dengan hanya mengkonversi ke antrian dan dequeue sampai hanya jumlah item yang ingin Anda lewati yang tersisa.
sumber
Bisa jadi:
Saya kira itu harus seperti de "Di mana" tetapi menjaga urutan (?).
sumber
Jika kecepatan adalah persyaratan, cara jadul ini seharusnya menjadi yang tercepat, meskipun kodenya tidak semulus linq.
Ini mensyaratkan bahwa urutannya adalah array karena memiliki panjang yang tetap dan item yang diindeks.
sumber
Saya mungkin akan melakukan sesuatu seperti ini:
Ini adalah satu iterasi dengan cek bahwa itu bukan yang terakhir untuk setiap kali.
sumber