Giữ gìn trật tự với LINQ


361

Tôi sử dụng hướng dẫn LINQ to Object trên một mảng được sắp xếp. Những thao tác nào tôi không nên làm để đảm bảo thứ tự của mảng không bị thay đổi?

Câu trả lời:


645

Tôi đã kiểm tra các phương thức của System.Linq.Enumerable , loại bỏ bất kỳ kết quả nào trả về kết quả không phải là IEn. Tôi đã kiểm tra các nhận xét của từng để xác định thứ tự của kết quả sẽ khác với thứ tự của nguồn như thế nào.

Bảo quản trật tự tuyệt đối. Bạn có thể ánh xạ một phần tử nguồn theo chỉ mục đến một phần tử kết quả

  • Vô số
  • Diễn viên
  • Concat
  • Lựa chọn
  • ToArray
  • Liệt kê

Giữ gìn trật tự. Các yếu tố được lọc hoặc thêm, nhưng không được sắp xếp lại.

  • Khác biệt
  • Ngoại trừ
  • Giao nhau
  • OfType
  • Chuẩn bị (mới trong .net 4.7.1)
  • Nhảy
  • Bỏ qua
  • Lấy
  • Đi
  • Ở đâu
  • Zip (mới trong .net 4)

Phá hủy thứ tự - chúng tôi không biết thứ tự nào để mong đợi kết quả.

  • Từ điển
  • Tra cứu

Xác định lại thứ tự một cách rõ ràng - sử dụng chúng để thay đổi thứ tự của kết quả

  • Đặt bởi
  • OrderByDesceinating
  • Đảo ngược
  • Sau đó
  • Sau đó, tăng dần

Xác định lại thứ tự theo một số quy tắc.

  • GroupBy - Các đối tượng IGrouping được tạo ra theo thứ tự dựa trên thứ tự của các phần tử trong nguồn tạo ra khóa đầu tiên của mỗi IGrouping. Các yếu tố trong một nhóm được mang lại theo thứ tự chúng xuất hiện trong nguồn.
  • GroupJoin - GroupJoin bảo tồn thứ tự của các yếu tố bên ngoài và đối với mỗi yếu tố bên ngoài, thứ tự của các yếu tố phù hợp từ bên trong.
  • Tham gia - bảo tồn thứ tự của các yếu tố bên ngoài và đối với mỗi yếu tố này, thứ tự của các yếu tố phù hợp bên trong.
  • SelectMany - cho mỗi phần tử của nguồn, bộ chọn được gọi và một chuỗi các giá trị được trả về.
  • Liên minh - Khi liệt kê đối tượng được trả về bằng phương thức này, Union liệt kê thứ nhất và thứ hai theo thứ tự đó và mang lại từng phần tử chưa được sinh ra.

Chỉnh sửa: Tôi đã chuyển Phân biệt sang Bảo quản đơn hàng dựa trên việc triển khai này .

    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
Trên thực tế, tôi nghĩ rằng Phân biệt duy trì thứ tự gốc (lần đầu tiên được tìm thấy) - vì vậy {1,2,1,3,1,3,4,1,5} sẽ là {1,2,3,4,5}
Marc Gravell

10
msdn.microsoft.com/en-us/l Library / bb348436.aspx Phương thức <(Of <(TSource>)>) (IEnumerable <(Of <(TSource>)>)) trả về một chuỗi không có thứ tự không chứa các giá trị trùng lặp .
Amy B

12
Marc: những gì bạn nói có thể đúng, nhưng sẽ dựa vào hành vi đó là một ý tưởng tồi.
Amy B

4
@Amy B có nhưng nó không áp dụng cho Linq cho các đối tượng. Trong Linq to Sql, differ () đặt từ khóa riêng biệt vào sql được tạo và việc đặt hàng từ sql không được đảm bảo. Tôi rất muốn thấy việc triển khai linq cho các đối tượng không giữ trật tự và hiệu quả hơn so với việc thực hiện trật tự. Ví dụ: bạn có thể sử dụng toàn bộ đầu vào và đặt nó vào một hàm băm, sau đó mang lại các giá trị bằng cách liệt kê hàm băm (mất thứ tự), nhưng điều đó còn tệ hơn. Vì vậy, yeah, tôi không nhớ bất chấp các tài liệu tất cả bây giờ và sau đó :)
dan

4
Có thể tài liệu (đối với Distinctphương pháp) chỉ có nghĩa là nói "chưa được sắp xếp", chứ không phải "theo thứ tự không thể đoán trước". Tôi muốn nói Distinctthuộc về loại lọc ở trên, giống như Where.
Jeppe Stig Nielsen

34

Bạn đang thực sự nói về SQL, hay về mảng? Nói cách khác, bạn đang sử dụng LINQ to SQL hay LINQ to Object?

Các toán tử LINQ to Object không thực sự thay đổi nguồn dữ liệu gốc của họ - họ xây dựng các chuỗi được hỗ trợ hiệu quả bởi nguồn dữ liệu. Các hoạt động duy nhất thay đổi thứ tự là OrderBy / OrderByDesceinating / ThenBy / ThenByDesceinating - và thậm chí sau đó, chúng hoạt động ổn định cho các phần tử có thứ tự như nhau. Tất nhiên, nhiều thao tác sẽ lọc ra một số phần tử, nhưng các phần tử được trả về sẽ theo cùng một thứ tự.

Nếu bạn chuyển đổi sang cấu trúc dữ liệu khác, ví dụ như với ToLookup hoặc ToDipedia, tôi không tin rằng trật tự được giữ nguyên tại thời điểm đó - nhưng dù sao thì điều đó cũng hơi khác. (Tôi tin rằng thứ tự ánh xạ các giá trị cho cùng một khóa được tìm kiếm để tra cứu.)


vì vậy OrderBy là một loại ổn định, nên: seq.OrderBy (_ => _.Key) sẽ đặt các phần tử vào chính xác theo thứ tự như seq.groupBy (_ => _.Key) .SelectMany (_ => _ ). Đúng không?
dmg

1
@dmg: Không, nó sẽ không. Chỉ cần làm GroupBytheo SelectManysẽ đưa ra kết quả được nhóm theo khóa, nhưng không theo thứ tự khóa tăng dần ... nó sẽ cung cấp cho họ theo thứ tự các khóa ban đầu xảy ra.
Jon Skeet

bạn đang nói rằng LINQ to SQL không bảo lưu thứ tự?
cộng sinh

@symbiont: Trong nhiều hoạt động SQL có lệnh cấm rõ ràng để bắt đầu. Về cơ bản, tôi đang cố gắng chỉ thực hiện những lời hứa về những điều tôi có thể đảm bảo - chẳng hạn như LINQ to Object.
Jon Skeet

@JonSkeet Nếu tôi sử dụng OrderBy, nó có đảm bảo rằng các đối tượng có cùng khóa sẽ giữ nguyên chuỗi ban đầu của chúng ngoại trừ tất cả chúng đều cùng nhau. tức là: trong trường list<x> {a b c d e f g}hợp c, d, e đều có cùng khóa thì chuỗi kết quả sẽ chứa c, d, e cạnh nhau VÀ theo thứ tự c, d, e. Tôi dường như không thể tìm thấy một câu trả lời dựa trên MS phân loại.
Paulustrious

7

Nếu bạn đang làm việc trên một mảng, có vẻ như bạn đang sử dụng LINQ-to-Object chứ không phải SQL; bạn có thể xác nhận? Hầu hết các hoạt động LINQ không sắp xếp lại bất cứ thứ gì (đầu ra sẽ theo cùng thứ tự với đầu vào) - vì vậy đừng áp dụng một loại khác (OrderBy [Giảm dần] / ThenBy [Giảm dần]).

[sửa: như Jon nói rõ hơn; LINQ thường tạo ra một chuỗi mới , để lại dữ liệu gốc]

Lưu ý rằng việc đẩy dữ liệu vào một Dictionary<,>(ToDipedia) sẽ làm xáo trộn dữ liệu, vì từ điển không tôn trọng bất kỳ thứ tự sắp xếp cụ thể nào.

Nhưng những điều phổ biến nhất (Chọn, Ở đâu, Bỏ qua, Lấy) sẽ ổn.


Nếu tôi không nhầm, ToDictionary()chỉ đơn thuần là không hứa hẹn về thứ tự, nhưng trong thực tế vẫn duy trì thứ tự đầu vào (cho đến khi bạn loại bỏ thứ gì đó khỏi nó). Tôi không nói là dựa vào điều này, nhưng 'tranh giành' có vẻ không chính xác.
Timo

4

Tôi tìm thấy một câu trả lời tuyệt vời trong một câu hỏi tương tự tham khảo tài liệu chính thức. Để trích dẫn nó:

Đối với Enumerablephương pháp (LINQ to Objects, áp dụng cho List<T>), bạn có thể dựa vào thứ tự của các yếu tố được trả về bởi Select, Wherehoặc GroupBy. Đây không phải là trường hợp cho những thứ vốn đã không có thứ tự như ToDictionaryhoặc Distinct.

Từ tài liệu En Enableable.groupBy :

Các IGrouping<TKey, TElement>đối tượng được sinh ra theo thứ tự dựa trên thứ tự của các phần tử trong nguồn tạo ra khóa đầu tiên của mỗi phần tử IGrouping<TKey, TElement>. Các yếu tố trong một nhóm được mang lại theo thứ tự chúng xuất hiện source.

Điều này không nhất thiết đúng với IQueryable các phương thức mở rộng (các nhà cung cấp LINQ khác).

Nguồn: Các phương pháp vô số của LINQ có duy trì trật tự các yếu tố tương đối không?


2

Bất kỳ 'nhóm theo' hoặc 'thứ tự bởi' sẽ có thể thay đổi thứ tự.


0

Câu hỏi ở đây đặc biệt đề cập đến LINQ-to-Object.

Nếu bạn sử dụng LINQ-to-SQL thay vào đó, sẽ không có thứ tự nào trừ khi bạn áp đặt một thứ như:

mysqlresult.OrderBy(e=>e.SomeColumn)

Nếu bạn không làm điều này với LINQ-to-SQL thì thứ tự kết quả có thể khác nhau giữa các truy vấn tiếp theo, thậm chí của cùng một dữ liệu, có thể gây ra lỗi không liên tục.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.