4

I have an IEnumerable<string> being passed to me which has the following items:

  • HP
  • Canon
  • Lexmark
  • Samsung
  • Other

Now what I am trying to achieve is order the items alphabetically but I want to keep the other item last. I have gone ahead and used OrderBy(i => i)

which lists the items as follows:

  • Canon
  • HP
  • Lexmark
  • Other
  • Samsung

The desired result I want is

  • Canon
  • HP
  • Lexmark
  • Samsung
  • Other

I have tried using .OrderBy(i => i != "Other").ThenBy(i => i) but this puts Other item right at the top.

Can someone tell me how to achieve this please.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
Izzy
  • 6,740
  • 7
  • 40
  • 84

5 Answers5

5

You have to use OrderByDescending because true is "higher" than false

list.OrderByDescending(s => s != "Other")
    .ThenBy(s => s);

You could also use

list.OrderBy(s => s == "Other" ? 1 : 0)
    .ThenBy(s => s);

Since you have asked, here are extension methods which simplify it for future use:

public static IEnumerable<T> OrderFirst<T>(this IEnumerable<T> sequence, Func<T, bool> predicate)
{
    return sequence.OrderByDescending(predicate);
}

public static IEnumerable<T> OrderLast<T>(this IEnumerable<T> sequence, Func<T, bool> predicate)
{
    return sequence.OrderBy(predicate);
}

Example usage:

var list = new List<string> { "HP", "Other", "Samsung" };
var otherLast = list.OrderLast(s => s == "Other").ToList();
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • Thank for this. How would I turn it into an extension method becuase I will be doing it with a few other `IEnumerable` – Izzy Apr 27 '17 at 09:59
  • Or (just because it's slightly shorter) `list.OrderBy(s => s == "Other").ThenBy(s => s);` – Matthew Watson Apr 27 '17 at 10:05
  • @Code you don't need to do anything. You can use multiple fields with `OrderBy`, just like `Select`. If you use an expression in `OrderBy` that returns `(s=="Other",s)` or `Tuple.Create(s=="Other",s)`, you'll achieve what you want. It's equivalent to SQL's `ORDER BY a,b,c` – Panagiotis Kanavos Apr 27 '17 at 10:08
  • @MatthewWatson not shorter than `list.OrderBy(s=>(s=="Other",s))`, or the equivalent with Tuple.Create. I wonder what the performance difference would be though. Copying vs allocations vs (possibly) multiple iterations? – Panagiotis Kanavos Apr 27 '17 at 10:09
  • You've got a good point i'll stick with this – Izzy Apr 27 '17 at 10:13
4

I have tried using .OrderBy(i => i != "Other").ThenBy(i => i) but this puts Other item right at the top.

So you're almost there, you just need to reverse the sort order of your first criterion: Replace OrderBy by OrderByDesc or replace i != "Other" with i == "Other.

Of course, it does not hurt to be explicit: It's not obvious how Booleans are sorted, so I'd prefer:

.OrderBy(i != "Other" ? 1 : 2).ThenBy(i => i);
Heinzi
  • 167,459
  • 57
  • 363
  • 519
4

Another option using tuples:

list.OrderBy(s => Tuple.Create(s=="Other",s));

or, in C# 7 :

list.OrderBy(s => (s=="Other",s));

OrderBy expects an expression that returns the key by which to sort. If you want to sort by multiple fields, you need to return an object or tuple that contains the field's values. It's no different than Select().

Tuples are needed because they already implement IComparable. Returning an anonymous object would fail because it doesn't implement this interface

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
0

Try this:

 //Imagining your list like this :
 var myList = new List<string>() {"HP", "Canon", "Lexmark", "Samsung", "Other"};

  var other = myList.Where(ro => ro == "Other").FirstOrDefault();
  myList.Remove(other);
  var finalList = myList.OrderBy(ro => ro).ToList();
  finalList.Add(other);

I hope this helps.

Coskun Ozogul
  • 2,389
  • 1
  • 20
  • 32
0

You can also do this by adding the "Other" at the end after the Order By:

 IEnumerable<string> lst = new string[]
      {
         "HP", "Canon", "Lexmark", "Samsung", "Other"
      };

 lst = lst.Select(x => x).Where(x => x != "Other").OrderBy(x => x).ToList();
 lst = lst.Concat(new string[] {"Other"}).ToList();

Or in short:

lst = lst.Select(x => x).Where(x => x != "Other").OrderBy(x => x).Concat(new string[] {"Other"}).ToList();
Willy David Jr
  • 8,604
  • 6
  • 46
  • 57