รักษาลำดับด้วย LINQ


361

ฉันใช้ LINQ กับคำสั่ง Objects ในอาเรย์ที่สั่ง การดำเนินการใดที่ฉันไม่ควรทำเพื่อให้แน่ใจว่าลำดับของอาร์เรย์จะไม่เปลี่ยนแปลง?

คำตอบ:


645

ฉันตรวจสอบวิธีการของSystem.Linq.Enumerableยกเลิกสิ่งที่ส่งคืนผลลัพธ์ที่ไม่ใช่ IEnumerable ฉันตรวจสอบคำพูดของแต่ละคนเพื่อพิจารณาว่าลำดับของผลลัพธ์จะแตกต่างจากลำดับของแหล่งที่มาอย่างไร

รักษาคำสั่งซื้ออย่างแน่นอน คุณสามารถแมปองค์ประกอบที่มาด้วยดัชนีไปยังองค์ประกอบผลลัพธ์

  • AsEnumerable
  • โพลล์
  • concat
  • เลือก
  • toArray
  • ToList

รักษาคำสั่งซื้อ องค์ประกอบถูกกรองหรือเพิ่ม แต่ไม่ได้สั่งใหม่

  • แตกต่าง
  • ยกเว้น
  • ตัด
  • OfType
  • prepend (ใหม่ใน. net 4.7.1)
  • ข้าม
  • SkipWhile
  • เอา
  • TakeWhile
  • ที่ไหน
  • Zip (ใหม่ใน. net 4)

ทำลายคำสั่งซื้อ - เราไม่ทราบว่าจะสั่งซื้อผลลัพธ์ใด

  • ToDictionary
  • ToLookup

กำหนดคำสั่งใหม่อย่างชัดเจน - ใช้สิ่งเหล่านี้เพื่อเปลี่ยนลำดับของผลลัพธ์

  • สั่งโดย
  • OrderByDescending
  • ย้อนกลับ
  • ThenBy
  • ThenByDescending

กำหนดคำสั่งใหม่ตามกฎบางอย่าง

  • GroupBy - ออบเจ็กต์การจัดกลุ่ม IGG ได้รับการจัดเรียงตามลำดับขององค์ประกอบในแหล่งที่มาซึ่งสร้างคีย์แรกของการจัดกลุ่มแต่ละครั้ง องค์ประกอบในการจัดกลุ่มจะได้รับตามลำดับที่ปรากฏในแหล่งที่มา
  • GroupJoin - GroupJoin รักษาลำดับขององค์ประกอบด้านนอกและสำหรับแต่ละองค์ประกอบของนอกลำดับขององค์ประกอบที่ตรงกันจากภายใน
  • เข้าร่วม - รักษาลำดับขององค์ประกอบด้านนอกและสำหรับแต่ละองค์ประกอบเหล่านี้ลำดับขององค์ประกอบที่ตรงกันของภายใน
  • SelectMany - สำหรับแต่ละองค์ประกอบของแหล่งที่มาตัวเลือกจะถูกเรียกใช้และลำดับของค่าจะถูกส่งกลับ
  • ยูเนี่ยน - เมื่อวัตถุที่ส่งคืนโดยเมธอดนี้ถูกแจกแจงยูเนี่ยนจะแจกแจงลำดับที่หนึ่งและสองในลำดับนั้นและให้ผลตอบแทนแต่ละองค์ประกอบที่ยังไม่ได้รับการส่งมอบ

แก้ไข: ฉันย้ายส่วนที่แตกต่างไปยังการเก็บรักษาใบสั่งตามการใช้งานนี้

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

2
จริงๆแล้วฉันคิดว่า Distinct จะรักษาความสงบเรียบร้อย (พบครั้งแรก) - ดังนั้น {1,2,1,3,1,3,4,1,5}} จะเป็น {1,2,3,4,5}
Marc Gravell

10
msdn.microsoft.com/en-us/library/bb348436.aspx The Distinct <(Of <(TSource>)>>) (IEnumerable <(Of <(TSource>)>)) ส่งคืนลำดับที่ไม่เรียงลำดับซึ่งไม่มีค่าซ้ำกัน .
เอมี่บี

12
Marc: สิ่งที่คุณพูดอาจเป็นจริง แต่มันเป็นความคิดที่ดีที่จะพึ่งพาพฤติกรรมนั้น
เอมี่ B

4
@Amy B ใช่ แต่ใช้ไม่ได้กับ Linq กับ Objects ใน Linq ถึง Sql, ชัดเจน () ใส่คำสำคัญที่แตกต่างใน sql ที่สร้างขึ้นและไม่รับประกันการสั่งซื้อจาก sql ฉันสนใจที่จะเห็นการใช้งานที่แตกต่างกันสำหรับ linq กับวัตถุที่ไม่รักษาลำดับและมีประสิทธิภาพมากกว่าที่รักษาลำดับไว้ ตัวอย่างเช่นคุณสามารถใช้อินพุตทั้งหมดและวางไว้ใน hashset จากนั้นให้ผลค่าโดยการระบุ hashset (เสียคำสั่ง) แต่นั่นแย่กว่า เพื่อใช่ฉันไม่คิด defying เอกสารทุกขณะนี้แล้ว :)
แดน

4
บางทีเอกสาร (สำหรับDistinctวิธีการ) อาจหมายถึงการพูดว่า "ไม่ได้เรียงลำดับ" ไม่ใช่ "ตามลำดับที่คาดเดาไม่ได้" ผมว่าเป็นของประเภทการกรองข้างต้นเช่นเดียวกับDistinct Where
Jeppe Stig Nielsen

34

คุณกำลังพูดถึง SQL จริง ๆ หรือเกี่ยวกับอาร์เรย์ หากต้องการใช้อีกวิธีหนึ่งคุณใช้ LINQ กับ SQL หรือ LINQ กับวัตถุหรือไม่

ตัวดำเนินการ LINQ เป็นวัตถุไม่ได้เปลี่ยนแหล่งข้อมูลดั้งเดิมของพวกเขา - พวกเขาสร้างลำดับที่สำรองข้อมูลได้อย่างมีประสิทธิภาพโดยแหล่งข้อมูล การดำเนินการเดียวที่เปลี่ยนการสั่งซื้อคือ OrderBy / OrderByDescending / ThenBy / ThenByDescending - และแม้กระทั่งนั้นจะมีเสถียรภาพสำหรับองค์ประกอบที่สั่งซื้อเท่ากัน แน่นอนการดำเนินการจำนวนมากจะกรององค์ประกอบบางอย่าง แต่องค์ประกอบที่ส่งคืนจะอยู่ในลำดับเดียวกัน

หากคุณแปลงเป็นโครงสร้างข้อมูลที่แตกต่างกันเช่นด้วย ToLookup หรือ ToDictionary ฉันไม่เชื่อว่าคำสั่งซื้อจะได้รับการเก็บรักษาไว้ ณ จุดนั้น - แต่นั่นก็ค่อนข้างแตกต่างกันอยู่ดี (ลำดับของการจับคู่ค่ากับคีย์เดียวกันนั้นสงวนไว้สำหรับการค้นหาแม้ว่าฉันเชื่อ)


ดังนั้นเนื่องจาก OrderBy เป็นการจัดเรียงที่มีความเสถียรดังนั้น: seq.OrderBy (_ => _.Key) จะทำให้องค์ประกอบเข้าสู่ลำดับเดียวกันกับ seq.GroupBy (_ => _.Key) .SelectMany (_ => _ ) ถูกต้องไหม
dmg

1
@dmg: ไม่มันจะไม่ เพียงแค่GroupByตามด้วยSelectManyจะให้ผลลัพธ์ที่จัดกลุ่มตามคีย์ แต่ไม่เรียงลำดับคีย์ ... จะให้เรียงตามลำดับที่คีย์เกิดขึ้น
Jon Skeet

คุณพูดว่า LINQ ไปยัง SQL ไม่ได้สั่งซื้อไว้ล่วงหน้า?
symbiont

@symbiont: ในการดำเนินงาน SQL มีหลายเป็นคำสั่งไม่ดีที่กำหนดจะเริ่มต้นด้วย โดยทั่วไปฉันกำลังพยายามทำสัญญาเกี่ยวกับสิ่งที่ฉันสามารถรับประกันได้ - เช่น LINQ ไปยังวัตถุ
Jon Skeet

@JonSkeet ถ้าฉันใช้ OrderBy มันรับประกันได้ว่าวัตถุ 'n' ที่มีคีย์เดียวกันจะรักษาลำดับเดิมของพวกเขายกเว้นว่าพวกเขาทั้งหมดเข้าด้วยกัน ie.: list<x> {a b c d e f g}ถ้า c, d, e ทั้งหมดมีคีย์เดียวกันดังนั้นลำดับที่ได้จะมี c, d, e ติดกันและในลำดับ c, d, e ฉันไม่สามารถหาคำตอบจาก MS อย่างเด็ดขาด
Paulustrious

7

หากคุณกำลังทำงานกับอาเรย์ดูเหมือนว่าคุณกำลังใช้ LINQ-to-Objects ไม่ใช่ SQL; คุณยืนยันได้ไหม การทำงานของ LINQ ส่วนใหญ่จะไม่เรียงลำดับอะไรใหม่ (ผลลัพธ์จะอยู่ในลำดับเดียวกันกับอินพุต) - ดังนั้นอย่าใช้การเรียงลำดับอื่น (OrderB โดย [Descending] / จากนั้นโดย [Descending])

[แก้ไข: ตามที่จอนระบุไว้ชัดเจนยิ่งขึ้น; โดยทั่วไป LINQ จะสร้างลำดับใหม่โดยปล่อยให้ข้อมูลต้นฉบับอยู่คนเดียว

โปรดทราบว่าการกดข้อมูลลงในDictionary<,>(ToDictionary) จะแย่งข้อมูลเนื่องจากพจนานุกรมไม่เคารพลำดับการเรียงลำดับใด ๆ

แต่สิ่งที่พบบ่อยที่สุด (เลือก, ที่ไหน, ข้าม, รับ) ควรจะใช้ได้


หากฉันไม่ผิดพลาดToDictionary()เพียงแค่ไม่ได้สัญญาเกี่ยวกับคำสั่งซื้อ แต่ในทางปฏิบัติจะรักษาลำดับการป้อนข้อมูลไว้ (จนกว่าคุณจะลบบางสิ่งออกจากมัน) ฉันไม่ได้บอกว่าจะพึ่งพาสิ่งนี้ แต่ 'การแย่งชิง' ดูเหมือนจะไม่ถูกต้อง
Timo

4

ฉันพบคำตอบที่ดีในคำถามที่คล้ายกันซึ่งอ้างอิงเอกสารอย่างเป็นทางการ อ้างถึง:

สำหรับEnumerableวิธีการ (LINQ กับวัตถุที่ใช้กับList<T>) คุณสามารถพึ่งพาลำดับขององค์ประกอบที่ส่งกลับโดยSelect, หรือWhere GroupByกรณีนี้ไม่ได้สำหรับสิ่งที่ไม่เรียงลำดับโดยเนื้อแท้เหมือนหรือToDictionaryDistinct

จากเอกสารEnumerable.GroupBy :

วัตถุที่มีการให้ผลในลำดับที่ตามลำดับขององค์ประกอบในแหล่งที่ผลิตกุญแจดอกแรกของแต่ละคนIGrouping<TKey, TElement> องค์ประกอบในการจัดกลุ่มจะให้ผลตามลำดับที่ปรากฏในIGrouping<TKey, TElement>source

สิ่งนี้ไม่เป็นความจริงสำหรับIQueryableวิธีการขยาย (ผู้ให้บริการ LINQ อื่น ๆ )

แหล่งที่มา: วิธีที่นับได้ของ LINQ สามารถรักษาลำดับองค์ประกอบที่สัมพันธ์กันได้หรือไม่?


2

'จัดกลุ่มตาม' หรือ 'สั่งซื้อโดย' อาจมีการเปลี่ยนแปลงคำสั่งซื้อ


0

คำถามที่นี่อ้างถึง LINQ-to-Objects โดยเฉพาะ

หากคุณใช้ LINQ-to-SQL แทนจะไม่มีคำสั่งซื้อที่นั่นเว้นแต่คุณจะกำหนดสิ่งที่ต้องการ:

mysqlresult.OrderBy(e=>e.SomeColumn)

หากคุณไม่ทำเช่นนี้กับ LINQ-to-SQL ลำดับของผลลัพธ์อาจแตกต่างกันระหว่างคิวรีที่ตามมาแม้ในข้อมูลเดียวกันซึ่งอาจทำให้เกิดบั๊กที่ไม่สม่ำเสมอ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.