Cómo ordenar una <cadena> IEnumerable

Respuestas:

154

De la misma forma que ordenarías cualquier otro enumerable:

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

o

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

o (ignorando el caso)

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

Tenga en cuenta que, como es habitual con LINQ, esto crea un nuevo IEnumerable <T> que, cuando se enumera, devuelve los elementos del IEnumerable <T> original en orden ordenado. No ordena el IEnumerable <T> in situ.


Un IEnumerable <T> es de solo lectura, es decir, solo puede recuperar los elementos de él, pero no puede modificarlo directamente. Si desea ordenar una colección de cadenas en el lugar, primero debe ordenar la colección original que implementa IEnumerable <string>, o convertir una IEnumerable <string> en una colección ordenable:

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

Basado en tu comentario:

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

o

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

o (si luego desea agregar más elementos a la lista y mantenerla ordenada)

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

_components.Add("foo");
_components.Sort();
dtb
fuente
o myEnumerable.OrderByDescending (s => s).
Grozz
Entonces, _components = _components.OrderBy (s => s); ¿estaría bien?
CatZilla
@CatZilla: Eso debería funcionar, pero puede que no sea la mejor manera de resolver su problema real. ¿Qué es _components, de dónde se obtiene, cómo se usa?
dtb
1
@dtb: Oh, _components se llena desde un archivo XML precisamente de esta manera: _components = (de c en xml.Descendants ("componente") seleccione c.Value.ToString ()). Distinct (). ToList (); Y necesito ordenarlo.
CatZilla
2
OrderBydevuelve IOrderedEnumerable<T>. IOrderedEnumerable<T>deriva de IEnumerable<T>para que pueda usarse como IEnumerable<T>, pero extiende el tipo, permitiendo por ejemplo, el uso de ThenBy.
Maciej Hehl
12

Es imposible, pero no lo es.

Básicamente, cualquier método de clasificación lo copiará IEnumerableen un List, clasificará el Listy luego le devolverá la lista ordenada, que es un IEnumerablearchivo y un archivo IList.

Esto significa que pierde la propiedad "continuar infinitamente" de un IEnumerable, pero de todos modos no podría ordenar uno así.

James Curran
fuente
7
Tocar el asunto exacto. El propósito de IEnumerable es presentarle un identificador para una serie que puede iterar, de principio a fin, al continuar solicitando el elemento "siguiente". Esto significa que un IEnumerable se puede iterar parcialmente antes de que se conozcan todos los contenidos; no tiene que saber cuándo los ha revisado todos hasta que los haya hecho. Ordenar (como muchas cosas que Linq le permite hacer) requiere el conocimiento de toda la serie como una lista ordenada; el elemento que aparecería primero en una serie ordenada puede ser el último devuelto por una serie, y no lo sabrá a menos que sepa cuáles son todos los elementos.
KeithS
8
myEnumerable = myEnumerable.OrderBy(s => s);
Larsenal
fuente
2

No siempre podemos hacerlo en el lugar, pero detectamos cuando es posible:

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

Esto usa la siguiente estructura útil:

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);
  }
}
Jon Hanna
fuente
No estoy seguro de si recomendaría esto. Si tiene un IEnumerable <T> pero no conoce el tipo real que implementa, probablemente no debería modificarlo. Por cierto, Array.FunctorComparer <T> es interno.
dtb
Al modificar lo que tenemos, supuse que estaba implícito en la pregunta de buscar en el lugar; que lo implica. Es una razón para tener "InPlaceInCan" en el nombre del método; los nombres de los métodos pueden ser incluso más directos sobre los riesgos que la mejor documentación;) Sí, Array.FunctorComparer <T> es interno, pero es trivial. Lo puse porque es la mejor manera que se me ocurre en un ejemplo de "su comparador de functor que tiene en su conjunto de clases de ayuda".
Jon Hanna
@dtb pensándolo bien, cambié para usar el mío (eché un vistazo a Array.FunctorComparer nuevamente y prefiero el mío de todos modos)
Jon Hanna
¡Idea inteligente y naming! Pero ¿por qué el doble de fundición contra el patrón de listToSort = (src is List<T>) ? (List<T>)src : new List<T>(src);? ¿Qué hay de tenerlo como?listToSort = (src as List<T>); if (null == listToSort) listToSort = new List<T>(src);
Jeroen Wiert Pluimers
1
@JeroenWiertPluimers siempre existe el problema con el código de ejemplo; tal vez simplemente no me molesté porque lo anterior es un poco más corto y quería concentrarme en el asunto en cuestión, pero es bueno desalentar los malos hábitos incluso en los ejemplos.
Jon Hanna