เรียงลำดับรายการจากรหัสรายการอื่น


150

ฉันมีรายการที่มีตัวระบุบางอย่างเช่นนี้:

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

Morover ฉันมีรายการอีกรายการหนึ่ง<T>ซึ่งมีรหัสตามที่อธิบายไว้ข้างต้น

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

ฉันต้องรักษาลำดับที่เหมือนกันในทั้งสองคอลเล็กชันเพื่อให้รายการในList<T>ต้องอยู่ในตำแหน่งเดียวกันมากกว่าอันดับแรก (เนื่องจากเหตุผลในการให้คะแนนของเครื่องมือค้นหา) และกระบวนการนี้ไม่สามารถทำได้ในGetDocsFromDb()ฟังก์ชั่น

หากจำเป็นก็เป็นไปได้ที่จะเปลี่ยนรายการที่สองเป็นโครงสร้างอื่น ๆ ( Dictionary<long, T>ตัวอย่าง) แต่ฉันไม่ต้องการเปลี่ยนมัน

มีวิธีที่ง่ายและมีประสิทธิภาพในการทำ "ordenation ขึ้นอยู่กับบาง ID" กับ LINQ หรือไม่?


คุณมั่นใจได้หรือไม่ว่าทุกอย่างdocIdเกิดขึ้นหนึ่งครั้งdocsคุณสมบัติใดที่จะถือIdหรือจะเป็นตัวเลือกที่Func<T, long>จำเป็น?
Jodrell

รายการแรกแสดงถึง "รายการหลัก" หรือไม่? คำอื่นรายการที่สองจะเป็นชุดย่อยที่แสดงถึงส่วน (หรือทั้งหมด) ของรายการแรกหรือไม่
code4life

คำตอบ:


332
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();

@Kaf thats ทำไมฉัน upvoted เกินไปไม่พึ่งพารู้คุณสมบัติ ID Idเอกสารที่เรียกว่า มันไม่ได้ระบุไว้ในคำถาม
Jodrell

3
@ BorjaLópezโน้ตสั้น ๆ คุณพูดถึงประสิทธิภาพในคำถามของคุณ IndexOfเป็นที่ยอมรับอย่างสมบูรณ์สำหรับตัวอย่างของคุณและดีและเรียบง่าย หากคุณมีข้อมูลจำนวนมากคำตอบของฉันอาจเหมาะสมกว่า stackoverflow.com/questions/3663014/…
Jodrell

2
@DenysDenysenko Fantastic ขอบคุณมาก; สิ่งที่ฉันกำลังมองหา
Silkfire

3
ไม่ทำงานหากคุณมีองค์ประกอบในเอกสารที่ไม่มีรหัสในรายการสั่งซื้อ
Dan Hunex

4
ไม่มีประสิทธิภาพค่อนข้าง - IndexOf จะถูกเรียกสำหรับทุกองค์ประกอบในการเก็บรวบรวมแหล่งที่มาและ OrderBy มีการสั่งซื้อองค์ประกอบ วิธีแก้ปัญหาโดย @Jodrell นั้นเร็วกว่ามาก
sdds

25

เนื่องจากคุณไม่ได้ระบุ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];
    }
}

เป็นส่วนขยายทั่วไปสำหรับสิ่งที่คุณต้องการ

คุณสามารถใช้ส่วนขยายแบบนี้ได้

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

รุ่นที่ปลอดภัยกว่าอาจเป็น

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

ซึ่งจะทำงานหากไม่ได้ซิปตรงกับsourceorder


ฉันใช้โซลูชันนี้และใช้งานได้ เพียงแค่นั้นฉันต้องทำให้วิธีการคงที่และระดับคงที่
vinmm

5

คำตอบของ Jodrell นั้นดีที่สุด แต่จริงๆแล้วเขาได้ปรับใช้System.Linq.Enumerable.Joinใหม่ เข้าร่วมยังใช้การค้นหาและทำการเรียงลำดับแหล่งที่มา

    docIds.Join(
      docs,
      i => i,
      d => d.Id,
      (i, d) => d);

นั่นคือคำตอบที่เรากำลังมองหา
Orace

2
นี่เป็นเพียงการพิสูจน์ว่าการเข้าร่วมนั้นยากเกินไปที่จะเข้าใจเนื่องจากทุกคนยอมรับว่าการเขียนใหม่นั้นง่ายกว่า
PRMan

-3

วิธีการง่าย ๆ อย่างหนึ่งคือการซิปตามลำดับการสั่งซื้อ:

List<T> docs = GetDocsFromDb(...).Zip(docIds, Tuple.Create)
               .OrderBy(x => x.Item2).Select(x => x.Item1).ToList();

ทำไมสั่งหลังจากซิป?
Jodrell

เพราะZipรวมแต่ละดัชนี (เป็น Tuple) กับเอกสารในตำแหน่งเดียวกันในรายการที่สอดคล้องกัน จากนั้น OrderBy จะเรียงลำดับ Tuples ตามส่วนดัชนีแล้วเลือก digs เพียงเอกสารของเราจากรายการที่สั่งตอนนี้
Albin Sunnanbo

แต่ผลของการ GetDocsFromDb คือเรียงลำดับดังนั้นคุณจะสร้าง Tuples ที่ไม่เกี่ยวข้องกับItem1 Item2
Jodrell

1
ฉันคิดว่าสิ่งนี้จะให้ผลลัพธ์ที่ไม่ถูกต้องตั้งแต่สั่งซื้อ uppon id และไม่ใช่ดัชนี
Michael Logutov
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.