116

How can I sort an IEnumerable<string> alphabetically. Is this possible?

Edit: How would I write an in-place solution?

David Vogel
  • 465
  • 8
  • 23
CatZilla
  • 1,456
  • 3
  • 12
  • 13

4 Answers4

181

The same way you'd sort any other enumerable:

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

or

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

or (ignoring case)

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

Note that, as is usual with LINQ, this creates a new IEnumerable<T> which, when enumerated, returns the elements of the original IEnumerable<T> in sorted order. It does not sort the IEnumerable<T> in-place.


An IEnumerable<T> is read-only, that is, you can only retrieve the elements from it, but cannot modify it directly. If you want to sort a collection of strings in-place, you need to sort the original collection which implements IEnumerable<string>, or turn an IEnumerable<string> into a sortable collection first:

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

Based on your comment:

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

or

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

or (if you want to later add more items to the list and keep it sorted)

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

_components.Add("foo");
_components.Sort();
dtb
  • 213,145
  • 36
  • 401
  • 431
  • 1
    or myEnumerable.OrderByDescending(s => s). – Grozz Sep 02 '10 at 19:55
  • So, _components = _components.OrderBy(s => s); would be fine? – CatZilla Sep 02 '10 at 19:59
  • @CatZilla: That should work, but may not be the best way to solve your actual problem. What is _components, where do you get it from, how do you use it? – dtb Sep 02 '10 at 20:03
  • 1
    @dtb: Oh, _components is populated from an XML file precisely this way: _components = (from c in xml.Descendants("component") select c.Value.ToString()).Distinct().ToList(); And I need to sort it. – CatZilla Sep 02 '10 at 20:07
  • 2
    `OrderBy` returns `IOrderedEnumerable`. `IOrderedEnumerable` derives from `IEnumerable` so it can be used like `IEnumerable`, but it extends the type, allowing for example, for the use of `ThenBy`. – Maciej Hehl Sep 02 '10 at 20:11
14

It is impossible, but it isn't.

Basically, any sort method is going to copy your IEnumerable into a List, sort the List and then return to you the sorted list, which is an IEnumerable as well as an IList.

This means you lose the "continue infinitely" property of an IEnumerable, but then you couldn't sort one like that anyway.

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 7
    Right on. IEnumerable's purpose is to present you with a handle to a series that you can iterate, beginning to end, by continuing to ask for the "next" item. This means that an IEnumerable can be partially iterated before all the contents are known; you don't have to know when you have gone through them all until you have. Sorting (like many things Linq lets you do) requires knowledge of the entire series as an ordered list; the item that would appear first in a sorted series may be the last one returned by a series, and you won't know that unless you know what all the items are. – KeithS Sep 02 '10 at 20:18
8
myEnumerable = myEnumerable.OrderBy(s => s);
Larsenal
  • 49,878
  • 43
  • 152
  • 220
2

We can't always do it in-place, but we detect when it's possible:

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

This uses the following handy struct:

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
  • 110,372
  • 10
  • 146
  • 251
  • I'm not sure if I'd recommend this. If you have an IEnumerable but don't know the actual type that implements, you probably shouldn't be modifying it. Btw, Array.FunctorComparer is internal. – dtb Sep 02 '10 at 20:56
  • On modifying what we have, I took that to be implied in the question looking for in-place; that it implies it. It's a reason to have "InPlaceInCan" in the method name; method names can be even more upfront about risks than the best documentation ;) Yeah, Array.FunctorComparer is internal, but it's trivial. I put it in because it's the best way I could think of in an example for "your functor comparer you have in your go-to set of helper classes". – Jon Hanna Sep 02 '10 at 21:22
  • @dtb on second thoughts, changed to use my own (took a look at Array.FunctorComparer again and I prefer mine anyway!) – Jon Hanna Sep 02 '10 at 22:08
  • Smart idea and naming! But why the [double casting anti pattern](http://www.boyet.com/Articles/DoubleCastingAntiPattern.html) in `listToSort = (src is List) ? (List)src : new List(src);`? What about having it like `listToSort = (src as List); if (null == listToSort) listToSort = new List(src);` – Jeroen Wiert Pluimers Oct 17 '13 at 07:51
  • @JeroenWiertPluimers honestly, after 3 years I couldn't tell you if I had any reason for the above. Perhaps I was going for brevity of example. I agree on using *as*. I don't agree the double-cast is an anti-pattern; it's an approach that is less efficient as .NET operates, but there's no reason why in theory that couldn't be optimised for, and I'd only call something an anti-pattern if it's inherently bad, whereas this I'd just call an inefficient approach. – Jon Hanna Oct 17 '13 at 08:43
  • @JonHanna I was just wondering (: And I know that I frown about some of my old code too. Maybe anti-pattern is indeed too strong, but I see it way too often often in code (even my own old code before I learned it could be done better). It is indeed inefficient. – Jeroen Wiert Pluimers Oct 17 '13 at 11:31
  • 1
    @JeroenWiertPluimers there's always the issue with example code too; maybe I just didn't bother because the above is slightly shorter and I wanted to concentrate on the matter in hand, but it is good to discourage bad habits even in examples. – Jon Hanna Oct 17 '13 at 11:57
  • I totally agree. And if you frown about my yoda-coding style: [been bitten in various environments too often](http://wiert.me/2010/05/25/yoda-conditions-from-stackoverflow-new-programming-jargon-you-coined/). Coding style is very much up for debate (: Anyway: +1 from me. – Jeroen Wiert Pluimers Oct 17 '13 at 13:18