Cách sắp xếp <string> IEnumerable


98

Làm thế nào tôi có thể sắp xếp một IEnumerable<string>thứ tự bảng chữ cái. Điều này có khả thi không?

Chỉnh sửa: Làm thế nào tôi sẽ viết một giải pháp tại chỗ?

Câu trả lời:


154

Giống như cách bạn sắp xếp bất kỳ kiểu liệt kê nào khác:

var result = myEnumerable.OrderBy(s => s);

hoặc là

var result = from s in myEnumerable
             orderby s
             select s;

hoặc (bỏ qua chữ hoa chữ thường)

var result = myEnumerable.OrderBy(s => s,
                                  StringComparer.CurrentCultureIgnoreCase);

Lưu ý rằng, như thường lệ với LINQ, điều này tạo ra một IEnumerable <T> mới, khi được liệt kê, trả về các phần tử của IEnumerable <T> ban đầu theo thứ tự được sắp xếp. Nó không sắp xếp IEnumerable <T> tại chỗ.


IEnumerable <T> ở chế độ chỉ đọc, nghĩa là bạn chỉ có thể truy xuất các phần tử từ nó chứ không thể sửa đổi trực tiếp. Nếu bạn muốn sắp xếp một tập hợp các chuỗi tại chỗ, bạn cần phải sắp xếp tập hợp ban đầu thực hiện IEnumerable <string> hoặc biến IEnumerable <string> thành một tập hợp có thể sắp xếp trước:

List<string> myList = myEnumerable.ToList();
myList.Sort();

Dựa trên nhận xét của bạn:

_components = (from c in xml.Descendants("component")
               let value = (string)c
               orderby value
               select value
              )
              .Distinct()
              .ToList();

hoặc là

_components = xml.Descendants("component")
                 .Select(c => (string)c)
                 .Distinct()
                 .OrderBy(v => v)
                 .ToList();

hoặc (nếu bạn muốn sau này thêm nhiều mục hơn vào danh sách và giữ nó được sắp xếp)

_components = xml.Descendants("component")
                 .Select(c => (string)c)
                 .Distinct()
                 .ToList();

_components.Add("foo");
_components.Sort();

hoặc myEnumerable.OrderByDescending (s => s).
Grozz

Vì vậy, _components = _components.OrderBy (s => s); sẽ ổn chứ?
CatZilla

@CatZilla: Điều đó sẽ hiệu quả, nhưng có thể không phải là cách tốt nhất để giải quyết vấn đề thực tế của bạn. _Components là gì, bạn lấy nó từ đâu, bạn sử dụng nó như thế nào?
dtb

1
@dtb: Ồ, _components được điền chính xác từ tệp XML theo cách này: _components = (from c in xml.Descendants ("component") select c.Value.ToString ()). Distinction (). ToList (); Và tôi cần phải sắp xếp nó.
CatZilla

2
OrderBylợi nhuận IOrderedEnumerable<T>. IOrderedEnumerable<T>bắt nguồn từ IEnumerable<T>đó, nó có thể được sử dụng như IEnumerable<T>, nhưng nó mở rộng loại, ví dụ, cho phép sử dụng ThenBy.
Maciej Hehl

12

Nó là không thể, nhưng nó không phải là.

Về cơ bản, bất kỳ phương pháp sắp xếp nào cũng sẽ sao chép của bạn IEnumerablevào một List, sắp xếp Listvà sau đó trả lại cho bạn danh sách đã sắp xếp, là một IEnumerablecũng như một IList.

Điều này có nghĩa là bạn mất thuộc tính "tiếp tục vô hạn" của một thuộc tính IEnumerable, nhưng dù sao thì bạn cũng không thể sắp xếp một thuộc tính như vậy.


7
Đúng rồi. Mục đích của IEnumerable là cung cấp cho bạn một tay cầm cho một chuỗi mà bạn có thể lặp lại, bắt đầu đến kết thúc, bằng cách tiếp tục yêu cầu mục "tiếp theo". Điều này có nghĩa là IEnumerable có thể được lặp lại một phần trước khi tất cả nội dung được biết; bạn không cần phải biết khi nào bạn đã trải qua tất cả cho đến khi bạn có. Việc sắp xếp (giống như nhiều thứ Linq cho phép bạn làm) yêu cầu kiến ​​thức về toàn bộ chuỗi như một danh sách có thứ tự; mặt hàng sẽ xuất hiện đầu tiên trong một chuỗi đã sắp xếp có thể là mặt hàng cuối cùng được một loạt trả lại và bạn sẽ không biết điều đó trừ khi bạn biết tất cả các mục là gì.
KeithS


2

Không phải lúc nào chúng tôi cũng có thể làm điều đó tại chỗ, nhưng chúng tôi phát hiện ra khi có thể:

IEnumerable<T> SortInPlaceIfCan(IEnumerable<T> src, IComparer<T> cmp)
{
  List<T> listToSort = (src is List<T>) ? (List<T>)src : new List<T>(src);
  listToSort.Sort(cmp);
  return listToSort;
}
IEnumerable<T> SortInPlaceIfCan(IEnumerable<T> src, Comparison<T> cmp)
{
  return SortInPlaceIfCan(src, new FuncComparer<T>(cmp));
}
IEnumerable<T> SortInPlaceIfCan(IEnumerable<T> src)
{
  return SortInPlaceIfCan(src, Comparer<T>.Default);
}

Điều này sử dụng cấu trúc hữu ích sau:

internal struct FuncComparer<T> : IComparer<T>
{
  private readonly Comparison<T> _cmp;
  public FuncComparer(Comparison<T> cmp)
  {
      _cmp = cmp;
  }
  public int Compare(T x, T y)
  {
      return _cmp(x, y);
  }
}

Tôi không chắc liệu tôi có muốn giới thiệu điều này hay không. Nếu bạn có IEnumerable <T> nhưng không biết kiểu thực tế sẽ triển khai, có lẽ bạn không nên sửa đổi nó. Btw, Array.FunctorComparer <T> là nội bộ.
dtb

Khi sửa đổi những gì chúng tôi có, tôi đã ngụ ý điều đó trong câu hỏi tìm kiếm tại chỗ; mà nó ngụ ý nó. Đó là lý do để có "InPlaceInCan" trong tên phương thức; tên phương thức thậm chí có thể trả trước về rủi ro nhiều hơn so với tài liệu tốt nhất;) Vâng, Array.FunctorComparer <T> là nội bộ, nhưng nó tầm thường. Tôi đưa nó vào bởi vì đó là cách tốt nhất mà tôi có thể nghĩ ra trong một ví dụ cho "trình so sánh chức năng của bạn mà bạn có trong tập hợp các lớp trợ giúp cần sử dụng".
Jon Hanna

@dtb trên những suy nghĩ thứ hai, thay đổi sử dụng của riêng tôi (đã xem xét Array.FunctorComparer một lần nữa và tôi thích tôi anyway!)
Jon Hanna

Ý tưởng và đặt tên thông minh! Nhưng tại sao mẫu chống đúc đôi trong listToSort = (src is List<T>) ? (List<T>)src : new List<T>(src);? Còn về việc có nó nhưlistToSort = (src as List<T>); if (null == listToSort) listToSort = new List<T>(src);
Jeroen Wiert Pluimers

1
@JeroenWiertPluimers luôn có vấn đề với mã ví dụ; Có lẽ tôi không bận tâm vì phần trên hơi ngắn hơn và tôi muốn tập trung vào vấn đề trong tay, nhưng tốt nhất là bạn nên ngăn cản những thói quen xấu ngay cả trong ví dụ.
Jon Hanna
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.