Как отсортировать IEnumerable <string>

98

Как отсортировать по IEnumerable<string>алфавиту. Это возможно?

Изменить: как мне написать решение на месте?

CatZilla
источник

Ответы:

155

Так же, как и любое другое перечислимое:

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();
dtb
источник
или myEnumerable.OrderByDescending (s => s).
Grozz 02
Итак, _components = _components.OrderBy (s => s); было бы хорошо?
CatZilla,
@CatZilla: Это должно сработать, но может быть не лучшим способом решения вашей реальной проблемы. Что такое _components, откуда вы их берете, как ими пользоваться?
dtb 02
1
@dtb: О, _components заполняется из файла XML именно таким образом: _components = (из c в xml.Descendants («компонент») выберите c.Value.ToString ()). Distinct (). ToList (); И мне нужно это разобрать.
CatZilla 02
2
OrderByвозвращается IOrderedEnumerable<T>. IOrderedEnumerable<T>является производным от, IEnumerable<T>поэтому его можно использовать как подобный IEnumerable<T>, но он расширяет тип, позволяя, например, использовать ThenBy.
Maciej Hehl 02
12

Это невозможно, но это не так.

По сути, любой метод сортировки скопирует ваш IEnumerableв List, отсортирует, Listа затем вернет вам отсортированный список, который является IEnumerableкак файлом IList.

Это означает, что вы теряете свойство «продолжать бесконечно» IEnumerable, но вы все равно не сможете отсортировать его таким образом.

Джеймс Карран
источник
7
Право на. Цель IEnumerable - предоставить вам дескриптор серии, которую вы можете перебирать от начала до конца, продолжая запрашивать «следующий» элемент. Это означает, что IEnumerable может быть частично повторен до того, как станет известно все содержимое; вам не нужно знать, когда вы их все прошли, пока не пройдете. Сортировка (как и многое другое в Linq) требует знания всей серии в виде упорядоченного списка; элемент, который появится первым в отсортированной серии, может быть последним, возвращенным серией, и вы не узнаете этого, если не знаете, каковы все элементы.
KeithS 02
8
myEnumerable = myEnumerable.OrderBy(s => s);
Ларсенал
источник
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 02
При изменении того, что у нас есть, я решил, что это подразумевается в вопросе о поиске на месте; что это подразумевает это. Это причина наличия 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 тоже всегда есть проблема с примером кода; возможно, я просто не стал беспокоиться, потому что вышеприведенное немного короче, и я хотел сосредоточиться на рассматриваемом вопросе, но хорошо отговаривать от вредных привычек даже в примерах.
Джон Ханна