Urutkan daftar dari ID daftar lain

150

Saya punya daftar dengan beberapa pengidentifikasi seperti ini:

List<long> docIds = new List<long>() { 6, 1, 4, 7, 2 };

Selain itu, saya memiliki daftar <T>item lain, yang diwakili oleh id yang dijelaskan di atas.

List<T> docs = GetDocsFromDb(...)

Saya perlu menjaga urutan yang sama di kedua koleksi, sehingga barang-barang di List<T>harus di posisi yang sama daripada yang pertama (karena alasan penilaian mesin pencari). Dan proses ini tidak dapat dilakukan dalam GetDocsFromDb()fungsi.

Jika perlu, dimungkinkan untuk mengubah daftar kedua menjadi beberapa struktur lain ( Dictionary<long, T>misalnya), tetapi saya lebih suka untuk tidak mengubahnya.

Apakah ada cara sederhana dan efisien untuk melakukan ini "ordenation tergantung pada beberapa ID" dengan LINQ?

Borja López
sumber
apakah Anda yakin bahwa setiap docIdterjadi tepat sekali dalam docs, properti apa yang akan dimiliki Idatau akan Func<T, long>diperlukan pemilih ?
Jodrell
Apakah daftar pertama mewakili "daftar utama"? Dengan kata lain, akankah daftar kedua menjadi himpunan bagian yang mewakili sebagian (atau keseluruhan) dari daftar pertama?
code4life

Jawaban:

332
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();
Denys Denysenko
sumber
@ Kaf, itulah sebabnya saya ikut undian, tidak bergantung pada mengetahui properti ID dokumen disebut Id. Tidak ditentukan dalam pertanyaan.
Jodrell
3
@ BorjaLópez, pesan singkat. Anda menyebutkan efisiensi dalam pertanyaan Anda. IndexOfsangat dapat diterima untuk contoh Anda dan bagus dan sederhana. Jika Anda memiliki banyak data, jawaban saya mungkin lebih cocok. stackoverflow.com/questions/3663014/…
Jodrell
2
@DenysDenysenko Fantastis. Terima kasih banyak; persis apa yang saya cari.
Silkfire
3
tidak berfungsi jika Anda memiliki elemen dalam dokumen yang tidak memiliki id dalam daftar pemesanan
Dan Hunex
4
Cukup tidak efisien - IndexOf dipanggil untuk setiap elemen dalam kumpulan sumber dan OrderBy harus memesan elemen. Solusi oleh @Jodrell jauh lebih cepat.
sdds
25

Karena Anda tidak menentukan T,

IEnumerable<T> OrderBySequence<T, TId>(
       this IEnumerable<T> source,
       IEnumerable<TId> order,
       Func<T, TId> idSelector)
{
    var lookup = source.ToDictionary(idSelector, t => t);
    foreach (var id in order)
    {
        yield return lookup[id];
    }
}

Merupakan ekstensi generik untuk apa yang Anda inginkan.

Anda dapat menggunakan ekstensi seperti ini mungkin,

var orderDocs = docs.OrderBySequence(docIds, doc => doc.Id);

Versi yang lebih aman mungkin

IEnumerable<T> OrderBySequence<T, TId>(
       this IEnumerable<T> source,
       IEnumerable<TId> order,
       Func<T, TId> idSelector)
{
    var lookup = source.ToLookup(idSelector, t => t);
    foreach (var id in order)
    {
        foreach (var t in lookup[id])
        {
           yield return t;
        }
    }
}

yang akan bekerja jika sourcetidak zip persis dengan order.

Jodrell
sumber
Saya menggunakan solusi ini dan berhasil. Hanya saja, saya harus membuat metode statis dan statis kelas.
vinmm
5

Jawaban Jodrell adalah yang terbaik, tetapi sebenarnya dia menerapkan kembali System.Linq.Enumerable.Join. Bergabunglah juga menggunakan Pencarian dan terus memesan sumber.

    docIds.Join(
      docs,
      i => i,
      d => d.Id,
      (i, d) => d);
Kladzey
sumber
Itulah jawaban yang kami cari
Orace
2
Ini membuktikan bahwa Bergabung terlalu sulit untuk dipahami karena semua orang setuju bahwa menulis ulang itu lebih mudah.
PRMan
-3

Salah satu pendekatan sederhana adalah melakukan zip dengan urutan pemesanan:

List<T> docs = GetDocsFromDb(...).Zip(docIds, Tuple.Create)
               .OrderBy(x => x.Item2).Select(x => x.Item1).ToList();
Albin Sunnanbo
sumber
mengapa memesan setelah pos?
Jodrell
Karena Zipmenggabungkan setiap indeks (ke dalam Tuple) dengan dokumen di posisi yang sama dalam daftar yang sesuai. Kemudian OrderBy mengurutkan Tuples berdasarkan bagian indeks dan kemudian pilih menggali dokumen kami hanya dari daftar sekarang dipesan.
Albin Sunnanbo
tetapi, hasil dari GetDocsFromDb adalah unordered sehingga Anda akan membuat Tuples di mana Item1tidak terkait Item2.
Jodrell
1
Saya pikir ini akan menghasilkan hasil yang salah karena memesan berkinerja baik dan bukan indeks.
Michael Logutov