Come ordinare un IEnumerable <string>


98

Come posso ordinare in IEnumerable<string>ordine alfabetico. È possibile?

Modifica: come scriverei una soluzione sul posto?

Risposte:


154

Nello stesso modo in cui ordineresti qualsiasi altro enumerabile:

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

o

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

o (ignorando maiuscole e minuscole)

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

Si noti che, come al solito con LINQ, questo crea un nuovo IEnumerable <T> che, quando enumerato, restituisce gli elementi dell'originale IEnumerable <T> in ordine ordinato. Non ordina IEnumerable <T> sul posto.


Un IEnumerable <T> è di sola lettura, ovvero è possibile solo recuperare gli elementi da esso, ma non modificarlo direttamente. Se si desidera ordinare una raccolta di stringhe sul posto, è necessario ordinare la raccolta originale che implementa IEnumerable <string> o prima trasformare un IEnumerable <string> in una raccolta ordinabile:

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

In base al tuo commento:

_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();

oppure (se desideri aggiungere in seguito più elementi all'elenco e mantenerlo ordinato)

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

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

o myEnumerable.OrderByDescending (s => s).
Grozz

Quindi, _components = _components.OrderBy (s => s); andrebbe bene?
CatZilla

@ CatZilla: dovrebbe funzionare, ma potrebbe non essere il modo migliore per risolvere il tuo problema reale. Cos'è _components, da dove lo prendi, come lo usi?
dtb

1
@dtb: Oh, _components è popolato da un file XML proprio in questo modo: _components = (from c in xml.Descendants ("component") select c.Value.ToString ()). Distinct (). ToList (); E ho bisogno di risolverlo.
CatZilla

2
OrderByritorna IOrderedEnumerable<T>. IOrderedEnumerable<T>deriva da IEnumerable<T>quindi può essere utilizzato come IEnumerable<T>, ma ne estende la tipologia, consentendo ad esempio l'utilizzo di ThenBy.
Maciej Hehl

12

È impossibile, ma non lo è.

Fondamentalmente, qualsiasi metodo di ordinamento copierà il tuo IEnumerablein un List, ordina il Liste poi ti restituisce l'elenco ordinato, che è IEnumerableun file IList.

Ciò significa che perdi la proprietà "continua infinitamente" di un IEnumerable, ma non puoi comunque ordinarne uno in quel modo.


7
Proprio su. Lo scopo di IEnumerable è di presentarti un handle per una serie che puoi iterare, dall'inizio alla fine, continuando a chiedere l'elemento "successivo". Ciò significa che un IEnumerable può essere parzialmente iterato prima che tutti i contenuti siano noti; non devi sapere quando li hai affrontati tutti finché non lo hai fatto. L'ordinamento (come molte cose che Linq ti permette di fare) richiede la conoscenza dell'intera serie come un elenco ordinato; l'elemento che apparirebbe per primo in una serie ordinata potrebbe essere l'ultimo restituito da una serie, e non lo saprai a meno che tu non sappia quali sono tutti gli elementi.
KeithS


2

Non possiamo sempre farlo sul posto, ma rileviamo quando è possibile:

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

Questo usa la seguente pratica struttura:

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

Non sono sicuro se lo consiglierei. Se hai un IEnumerable <T> ma non conosci il tipo effettivo che implementa, probabilmente non dovresti modificarlo. A proposito, Array.FunctorComparer <T> è interno.
dtb

Modificando ciò che abbiamo, ho pensato che fosse implicito nella domanda che cercava sul posto; che lo implica. È un motivo per avere "InPlaceInCan" nel nome del metodo; i nomi dei metodi possono essere ancora più precisi sui rischi rispetto alla migliore documentazione;) Sì, Array.FunctorComparer <T> è interno, ma è banale. L'ho inserito perché è il modo migliore a cui potrei pensare in un esempio per "il tuo funtore comparer che hai nel tuo set di classi di supporto".
Jon Hanna

@dtb ripensandoci, cambiato per usare il mio (ho dato di nuovo un'occhiata ad Array.FunctorComparer e preferisco il mio comunque!)
Jon Hanna

Idea e denominazione intelligenti! Ma perché la doppia colata modello anti in listToSort = (src is List<T>) ? (List<T>)src : new List<T>(src);? Che ne dici di averlo comelistToSort = (src as List<T>); if (null == listToSort) listToSort = new List<T>(src);
Jeroen Wiert Pluimers

1
@JeroenWiertPluimers c'è sempre il problema anche con il codice di esempio; forse non mi sono preoccupato perché quanto sopra è leggermente più breve e volevo concentrarmi sull'argomento in questione, ma è bene scoraggiare le cattive abitudini anche negli esempi.
Jon Hanna
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.