Mempertahankan pesanan dengan LINQ

361

Saya menggunakan instruksi LINQ to Objects pada array yang dipesan. Operasi mana yang tidak harus saya lakukan untuk memastikan urutan array tidak berubah?

Matthieu Durut
sumber

Jawaban:

645

Saya memeriksa metode System.Linq.Enumerable , membuang semua yang mengembalikan hasil non-IEnumerable. Saya memeriksa komentar masing-masing untuk menentukan bagaimana urutan hasil akan berbeda dari urutan sumber.

Mempertahankan Ketertiban sepenuhnya. Anda dapat memetakan elemen sumber dengan indeks ke elemen hasil

  • AsEnumerable
  • Pemeran
  • Concat
  • Pilih
  • ToArray
  • Daftar

Mempertahankan Ketertiban. Elemen disaring atau ditambahkan, tetapi tidak dipesan ulang.

  • Berbeda
  • Kecuali
  • Memotong
  • Jenis
  • Prepend (baru dalam .net 4.7.1)
  • Melewatkan
  • SkipWhile
  • Mengambil
  • Ambillah
  • Dimana
  • Zip (baru di .net 4)

Hancurkan Pesanan - kami tidak tahu pesanan apa yang diharapkan terjadi.

  • ToDictionary
  • Untuk mencari

Tetapkan Ulang Pesanan Secara Eksplisit - gunakan ini untuk mengubah urutan hasilnya

  • Dipesan oleh
  • OrderByDescending
  • Balik
  • Lalu oleh
  • ThenByDescending

Mendefinisikan ulang Pesanan sesuai dengan beberapa aturan.

  • GroupBy - Objek IGrouping dihasilkan dalam urutan berdasarkan urutan elemen dalam sumber yang menghasilkan kunci pertama dari setiap IGrouping. Elemen dalam pengelompokan dihasilkan dalam urutan yang muncul di sumber.
  • GroupJoin - GroupJoin mempertahankan urutan elemen luar, dan untuk setiap elemen luar, urutan elemen yang cocok dari dalam.
  • Bergabung - menjaga urutan elemen luar, dan untuk masing-masing elemen ini, urutan elemen yang cocok dari dalam.
  • SelectMany - untuk setiap elemen sumber, pemilih dipanggil dan urutan nilai dikembalikan.
  • Union - Ketika objek yang dikembalikan oleh metode ini disebutkan, Union menyebutkan pertama dan kedua dalam urutan itu dan menghasilkan setiap elemen yang belum dihasilkan.

Sunting: Saya telah memindahkan Distinct to Preserving order berdasarkan pada implementasi ini .

    private static IEnumerable<TSource> DistinctIterator<TSource>
      (IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
    {
        Set<TSource> set = new Set<TSource>(comparer);
        foreach (TSource element in source)
            if (set.Add(element)) yield return element;
    }
Amy B
sumber
2
Sebenarnya, saya pikir Distinct mempertahankan pesanan asli (pertama kali ditemukan) - jadi {1,2,1,3,1,3,4,1,5} adalah {1,2,3,4,5}
Marc Gravell
10
msdn.microsoft.com/en-us/library/bb348436.aspx Perbedaan metode <(Dari <(TSource>)>) (IEnumerable <(Dari <(TSource>)>)) mengembalikan urutan tidak berurutan yang tidak mengandung nilai duplikat .
Amy B
12
Marc: apa yang Anda katakan bisa benar, tetapi itu adalah ide yang buruk untuk mengandalkan perilaku itu.
Amy B
4
@Amy B ya tapi itu tidak berlaku untuk Linq to Objects. Di Linq ke Sql, berbeda () menempatkan kata kunci yang berbeda ke dalam sql yang dihasilkan, dan pemesanan dari sql tidak dijamin. Saya akan tertarik untuk melihat implementasi yang berbeda untuk LINQ untuk objek yang tidak menjaga ketertiban dan lebih efisien daripada yang menjaga ketertiban. Misalnya, Anda dapat menggunakan seluruh input dan memasukkannya dalam hashset, lalu menghasilkan nilai dengan menghitung hashset (kehilangan pesanan), tetapi itu lebih buruk. Jadi ya, saya tidak keberatan menentang dokumentasi sesekali :)
dan
4
Mungkin dokumentasi (untuk Distinctmetode) hanya bermaksud mengatakan "tidak disortir", bukan "dalam urutan yang tidak terduga". Saya akan mengatakan Distinctmilik kategori penyaringan di atas, sama seperti Where.
Jeppe Stig Nielsen
34

Apakah Anda benar-benar berbicara tentang SQL, atau tentang array? Dengan kata lain, apakah Anda menggunakan LINQ ke SQL atau LINQ ke Objek?

Operator LINQ to Objects tidak benar-benar mengubah sumber data asli mereka - mereka membangun urutan yang secara efektif didukung oleh sumber data. Satu-satunya operasi yang mengubah urutan adalah OrderBy / OrderByDescending / ThenBy / ThenByDescending - dan bahkan kemudian, itu stabil untuk elemen-elemen yang dipesan sama. Tentu saja, banyak operasi akan menyaring beberapa elemen, tetapi elemen yang dikembalikan akan berada dalam urutan yang sama.

Jika Anda mengonversi ke struktur data yang berbeda, mis. Dengan ToLookup atau ToDictionary, saya tidak yakin pesanan tetap dipertahankan pada saat itu - tetapi itu agak berbeda pula. (Namun, urutan pemetaan nilai untuk kunci yang sama dipertahankan untuk pencarian, saya yakin.)

Jon Skeet
sumber
jadi karena OrderBy adalah jenis yang stabil, maka: seq.OrderBy (_ => _.Key) akan meletakkan elemen ke dalam urutan yang sama persis dengan seq.GroupBy (_ => _.Key) .Pilih Banyak (_ => _ ). Apakah itu benar?
dmg
1
@ dmg: Tidak, tidak akan. Cukup GroupBydiikuti oleh SelectManyakan memberikan hasil yang dikelompokkan berdasarkan kunci, tetapi tidak dalam urutan kunci naik ... itu akan memberi mereka dalam urutan di mana kunci awalnya terjadi.
Jon Skeet
Anda mengatakan bahwa LINQ ke SQL tidak memesan pemelihara?
symbiont
@symbiont: Dalam banyak operasi SQL ada adalah agar tidak ada yang terdefinisi dengan baik untuk memulai dengan. Pada dasarnya saya hanya mencoba membuat janji tentang hal-hal yang dapat saya jamin - seperti LINQ ke Objek.
Jon Skeet
@JonSkeet Jika saya menggunakan OrderBy apakah itu menjamin bahwa 'n' objek dengan kunci yang sama akan mempertahankan urutan aslinya kecuali bahwa mereka semua bersama-sama. ie.:in list<x> {a b c d e f g}jika c, d, e semuanya memiliki kunci yang sama maka urutan yang dihasilkan akan berisi c, d, e di samping satu sama lain DAN dalam urutan c, d, e. Saya sepertinya tidak dapat menemukan jawaban berdasarkan kategori MS.
Paulustrious
7

Jika Anda bekerja pada sebuah array, sepertinya Anda menggunakan LINQ-to-Objects, bukan SQL; Bisakah kamu mengkonfirmasi? Sebagian besar operasi LINQ tidak memesan ulang apa pun (output akan berada dalam urutan yang sama dengan input) - jadi jangan menerapkan jenis lain (OrderBy [Turun] / ThenBy [Turun]).

[sunting: seperti yang Jon jelaskan; LINQ umumnya membuat urutan baru , meninggalkan data asli sendirian]

Perhatikan bahwa memasukkan data ke Dictionary<,>(ToDictionary) akan mengacak data, karena kamus tidak menghormati urutan pengurutan tertentu.

Tetapi hal yang paling umum (Pilih, Di Mana, Lewati, Ambil) harus baik-baik saja.

Marc Gravell
sumber
Jika saya tidak salah, ToDictionary()semata-mata tidak membuat janji tentang pesanan, tetapi dalam praktiknya mempertahankan urutan input (sampai Anda menghapus sesuatu dari itu). Saya tidak mengatakan mengandalkan ini, tetapi 'berebut' tampaknya tidak akurat.
Timo
4

Saya menemukan jawaban yang bagus dalam pertanyaan serupa yang merujuk pada dokumentasi resmi. Mengutipnya:

Untuk Enumerablemetode (LINQ untuk Objects, yang berlaku untuk List<T>), Anda dapat mengandalkan urutan elemen dikembalikan oleh Select, Whereatau GroupBy. Ini bukan kasus untuk hal-hal yang secara inheren tidak teratur seperti ToDictionaryatau Distinct.

Dari dokumentasi Enumerable.GroupBy :

The IGrouping<TKey, TElement>objek yang dihasilkan dalam urutan berdasarkan urutan unsur-unsur dalam sumber yang menghasilkan kunci pertama dari setiap IGrouping<TKey, TElement>. Elemen-elemen dalam suatu pengelompokan dihasilkan dalam urutan kemunculannya source.

Ini tidak selalu benar untuk IQueryablemetode ekstensi (penyedia LINQ lainnya).

Sumber: Apakah Metode Enumerable LINQ Menjaga Urutan Relatif Unsur?

Curtis Yallop
sumber
2

'Grup oleh' atau 'pesanan oleh' apa pun mungkin akan mengubah urutan.

leppie
sumber
0

Pertanyaan di sini secara khusus mengacu pada LINQ-to-Objects.

Jika Anda menggunakan LINQ-to-SQL bukan tidak ada pesanan di sana kecuali Anda memaksakannya dengan sesuatu seperti:

mysqlresult.OrderBy(e=>e.SomeColumn)

Jika Anda tidak melakukan ini dengan LINQ-to-SQL, maka urutan hasil dapat bervariasi di antara kueri berikutnya, bahkan dari data yang sama, yang dapat menyebabkan bug yang terputus-putus.

andrew pate
sumber