Urutan LINQ berdasarkan kolom nol di mana urutan naik dan nol harus terakhir


144

Saya mencoba mengurutkan daftar produk berdasarkan harganya.

Kumpulan hasil perlu mencantumkan produk menurut harga dari rendah ke tinggi menurut kolom LowestPrice. Namun, kolom ini tidak dapat diisi.

Saya dapat mengurutkan daftar dalam urutan seperti ini:

var products = from p in _context.Products
   where p.ProductTypeId == 1
   orderby p.LowestPrice.HasValue descending
   orderby p.LowestPrice descending
   select p;

// returns:    102, 101, 100, null, null

Namun saya tidak tahu bagaimana mengurutkan ini dalam urutan naik.

// i'd like: 100, 101, 102, null, null

12
orderby p.LowestPrice ?? Int.MaxValue;adalah cara yang sederhana.
PostMan

3
@ PostMan: Ya, sederhana, ini mencapai hasil yang benar, tetapi OrderByDescending, ThenBylebih jelas.
jason

@Jason, ya saya tidak tahu sintaks untuk orderby, dan dilacak samping mencarinya :)
PostMan

Jawaban:


168

Coba letakkan kedua kolom dalam urutan yang sama.

orderby p.LowestPrice.HasValue descending, p.LowestPrice

Jika tidak, setiap orderby adalah operasi terpisah pada koleksi yang menyusun ulang setiap waktu.

Ini harus mengurutkan yang memiliki nilai terlebih dahulu, "lalu" urutan nilainya.


24
Kesalahan umum, orang melakukan hal yang sama dengan Lamda Syntax - menggunakan .OrderBy dua kali, bukan .ThenBy.
DaveShaw

2
Ini Bekerja untuk mengurutkan bidang dengan nilai di atas dan bidang nol di bawah saya menggunakan ini: orderby p.LowestPrice == null, p.LowestPrice ascending Harapan membantu seseorang.
shaijut

@DaveShaw terima kasih atas tipnya - terutama komentarnya - sangat rapi - menyukainya
Demetris Leptos

89

Sangat membantu untuk memahami sintaks kueri LINQ dan bagaimana itu diterjemahkan ke panggilan metode LINQ.

Ternyata begitu

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending
               orderby p.LowestPrice descending
               select p;

akan diterjemahkan oleh kompilator ke

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .OrderByDescending(p => p.LowestPrice)
                       .Select(p => p);

Ini jelas bukan yang Anda inginkan. Ini macam oleh Product.LowestPrice.HasValuedi descendingurutan dan kemudian kembali macam seluruh koleksi oleh Product.LowestPricedi descendingurutan.

Apa yang kamu inginkan adalah

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .ThenBy(p => p.LowestPrice)
                       .Select(p => p);

yang bisa Anda peroleh menggunakan sintaks kueri

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending,
                       p.LowestPrice
               select p;

Untuk mengetahui detail terjemahan dari sintaks kueri ke panggilan metode, lihat spesifikasi bahasa. Sungguh. Membacanya.


4
+1 atau hanya ... jangan tulis sintaks kueri LINQ :) Meskipun demikian, penjelasan yang bagus
lihat

19

Solusi untuk nilai string sangat aneh:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString) 

Satu-satunya alasan yang berhasil adalah karena ekspresi pertama, OrderBy() ,, mengurutkan boolnilai: true/ false. falsehasilnya pergi dulu diikuti oleh truehasilnya (nullables) dan ThenBy()urutkan nilai bukan null menurut abjad.

e.g.: [null, "coconut", null, "apple", "strawberry"]
First sort: ["coconut", "apple", "strawberry", null, null]
Second sort: ["apple", "coconut", "strawberry", null, null]
Jadi, saya lebih suka melakukan sesuatu yang lebih mudah dibaca seperti ini:
.OrderBy(f => f.SomeString ?? "z")

Jika SomeStringnol, itu akan diganti dengan "z"dan kemudian mengurutkan semuanya menurut abjad.

CATATAN: Ini bukan solusi pamungkas karena "z"lebih dulu daripada nilai-z sepertizebra .

UPDATE 9/6/2016 - Tentang komentar @jornhd, ini benar-benar solusi yang bagus, tetapi masih sedikit rumit, jadi saya akan merekomendasikan untuk membungkusnya dalam kelas Ekstensi, seperti ini:

public static class MyExtensions
{
    public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, string> keySelector)
    {
        return list.OrderBy(v => keySelector(v) != null ? 0 : 1).ThenBy(keySelector);
    }
}

Dan gunakan sederhana seperti:

var sortedList = list.NullableOrderBy(f => f.SomeString);

2
Saya pikir ini lebih mudah dibaca, tanpa konstanta yang buruk: .OrderBy (f => f.SomeString! = Null? 0: 1) .ThenBy (f => f.SomeString)
jornhd

15

Saya punya pilihan lain dalam situasi ini. Daftar saya adalah objList, dan saya harus memesan tetapi null harus di akhir. keputusanku:

var newList = objList.Where(m=>m.Column != null)
                     .OrderBy(m => m.Column)
                     .Concat(objList.where(m=>m.Column == null));

Ini dapat bekerja dalam skenario di mana seseorang menginginkan hasil dengan nilai lain seperti 0 sebagai pengganti null.
Naresh Ravlani

Iya. Cukup ganti nol menjadi 0.
Gurgen Hovsepyan

ini adalah satu-satunya jawaban yang berhasil bagi saya, sisanya menyimpan nol di awal daftar.
BMills

9

Saya mencoba menemukan solusi LINQ untuk ini tetapi tidak dapat menyelesaikannya dari jawaban di sini.

Jawaban terakhir saya adalah:

.OrderByDescending(p => p.LowestPrice.HasValue).ThenBy(p => p.LowestPrice)

7

keputusanku:

Array = _context.Products.OrderByDescending(p => p.Val ?? float.MinValue)

7

Inilah yang saya dapatkan karena saya menggunakan metode ekstensi dan juga item saya adalah string, jadi tidak .HasValue:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString)

Ini bekerja dengan objek LINQ 2 di memori. Saya tidak mengujinya dengan EF atau DB ORM.


0

Di bawah ini adalah metode ekstensi untuk memeriksa null jika Anda ingin mengurutkan pada properti turunan dari keySelector.

public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, object> parentKeySelector, Func<T, object> childKeySelector)
{
    return list.OrderBy(v => parentKeySelector(v) != null ? 0 : 1).ThenBy(childKeySelector);
}

Dan gunakan sederhana seperti:

var sortedList = list.NullableOrderBy(x => x.someObject, y => y.someObject?.someProperty);

0

Berikut cara lain:

//Acsending
case "SUP_APPROVED_IND": qry =
                            qry.OrderBy(r => r.SUP_APPROVED_IND.Trim() == null).
                                    ThenBy(r => r.SUP_APPROVED_IND);

                            break;
//….
//Descending
case "SUP_APPROVED_IND": qry =
                            qry.OrderBy(r => r.SUP_APPROVED_IND.Trim() == null).
                                    ThenByDescending(r => r.SUP_APPROVED_IND); 

                            break;

SUP_APPROVED_IND is char(1) in Oracle db.

Catatan yang r.SUP_APPROVED_IND.Trim() == nulldiperlakukan seperti trim(SUP_APPROVED_IND) is nulldi Oracle db.

Lihat ini untuk detailnya: Bagaimana saya bisa meminta nilai null dalam kerangka entitas?


0

Opsi Lain (berguna untuk skenario kami):

Kami memiliki Tabel Pengguna, menyimpan ADName, LastName, FirstName

  • Pengguna harus sesuai abjad
  • Akun tanpa First- / LastName juga, berdasarkan ADName mereka - tetapi di akhir User-List
  • Pengguna Dummy dengan ID "0" ("Tidak Ada Pilihan") Harus selalu paling atas.

Kami mengubah skema tabel dan menambahkan Kolom "SortIndex", yang mendefinisikan beberapa grup penyortiran. (Kami menyisakan jarak 5, jadi kami bisa memasukkan grup nanti)

ID | ADName |      First Name | LastName | SortIndex
0    No Selection  null         null     | 0
1    AD\jon        Jon          Doe      | 5
3    AD\Support    null         null     | 10     
4    AD\Accounting null         null     | 10
5    AD\ama        Amanda       Whatever | 5

Sekarang, berdasarkan kueri:

SELECT * FROM User order by SortIndex, LastName, FirstName, AdName;

dalam Metode Ekspresi:

db.User.OrderBy(u => u.SortIndex).ThenBy(u => u.LastName).ThenBy(u => u.FirstName).ThenBy(u => u.AdName).ToList();

yang menghasilkan hasil yang diharapkan:

ID | ADName |      First Name | LastName | SortIndex
0    No Selection  null         null     | 0
5    AD\ama        Amanda       Whatever | 5
1    AD\jon        Jon          Doe      | 5
4    AD\Accounting null         null     | 10
3    AD\Support    null         null     | 10     
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.