Як відсортувати IEnumerable <string>


98

Як я можу відсортувати за IEnumerable<string>алфавітом. Чи можливо це?

Редагувати: Як я можу написати рішення на місці?

Відповіді:


154

Так само, як ви сортуєте будь-яку іншу кількість:

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

або

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

або (ігноруючи регістр)

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

Зверніть увагу, що, як це зазвичай буває у LINQ, це створює новий IEnumerable <T>, який при перерахуванні повертає елементи вихідного IEnumerable <T> у відсортованому порядку. Це не сортує IEnumerable <T> на місці.


IEnumerable <T> призначений лише для читання, тобто ви можете лише отримати елементи з нього, але не можете змінити його безпосередньо. Якщо ви хочете відсортувати колекцію рядків на місці, вам потрібно відсортувати вихідну колекцію, яка реалізує IEnumerable <string>, або перетворити IEnumerable <string> у сортувальну колекцію спочатку:

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

На основі вашого коментаря:

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

або

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

або (якщо ви хочете пізніше додати більше елементів до списку та зберегти його сортуванням)

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

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

або myEnumerable.OrderByDescending (s => s).
Grozz

Отже, _components = _components.OrderBy (s => s); було б добре?
CatZilla

@CatZilla: Це має спрацювати, але може бути не найкращим способом вирішити вашу справжню проблему. Що таке _компоненти, звідки ви його берете, як використовуєте?
dtb

1
@dtb: О, _components заповнюється з XML-файлу саме таким чином: _components = (з c у xml.Descendants ("компонент") виберіть c.Value.ToString ()). Distinct (). ToList (); І мені потрібно це відсортувати.
CatZilla

2
OrderByповертається IOrderedEnumerable<T>. IOrderedEnumerable<T>походить від, IEnumerable<T>тому його можна використовувати як IEnumerable<T>, але він розширює тип, дозволяючи, наприклад, для використання ThenBy.
Maciej Hehl

12

Це неможливо, але це не так.

В основному, будь-який метод сортування збирається скопіювати ваш файл IEnumerableу List, відсортувати Listі повернути вам відсортований список, який є IEnumerableяк, так і IList.

Це означає, що ви втрачаєте властивість "продовжувати нескінченно" IEnumerable, але тоді ви все одно не можете відсортувати такий.


7
Прямо на. Мета IEnumerable - представити вам дескриптор серії, яку ви можете повторити, починаючи до кінця, продовжуючи запитувати "наступний" елемент. Це означає, що IEnumerable може бути частково повторений до того, як буде відомий весь вміст; вам не потрібно знати, коли ви пережили їх усі, поки не отримаєте. Сортування (як і багато іншого, що дозволяє Linq) вимагає знання всієї серії як упорядкованого списку; елемент, який з’явиться першим у відсортованій серії, може бути останнім, що повертається серією, і ви цього не дізнаєтесь, якщо не знаєте, що це за елементи.
KeithS


2

Ми не завжди можемо зробити це на місці, але виявляємо, коли це можливо:

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

Для цього використовується така зручна структура:

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

Я не впевнений, чи рекомендував би я це. Якщо у вас є IEnumerable <T>, але ви не знаєте фактичного типу, який реалізується, ви, мабуть, не повинні його змінювати. До речі, Array.FunctorComparer <T> внутрішній.
dtb

Змінюючи те, що ми маємо, я вважав, що це мається на увазі у питанні, яке шукає місце; що це передбачає. Це причина, коли в назві методу є "InPlaceInCan"; імена методів можуть бути навіть більш чіткими щодо ризиків, ніж найкраща документація;) Так, Array.FunctorComparer <T> є внутрішнім, але це тривіально. Я вклав його, тому що це найкращий спосіб, про який я міг придумати, на прикладі "вашого порівняльника функторів, який ви маєте у своєму наборі допоміжних класів".
Джон Ханна,

@dtb на роздуми, змінено на власний (знову подивився Array.FunctorComparer, і я все одно віддаю перевагу своєму!)
Джон Ханна,

Розумна ідея та найменування! Але чому подвійне лиття анти візерунком в listToSort = (src is List<T>) ? (List<T>)src : new List<T>(src);? А як щодо того, щоб це сподобалосьlistToSort = (src as List<T>); if (null == listToSort) listToSort = new List<T>(src);
Jeroen Wiert Pluimers

1
@JeroenWiertPluimers завжди є проблема і з прикладом коду; можливо, я просто не турбувався, тому що вищезазначене трохи коротше, і я хотів зосередитись на питанні, але це добре відмовляти від шкідливих звичок навіть на прикладах.
Джон Ханна,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.