Is there a way in Linq to do an OrderBy against a set of values (strings in this case) without knowing the order of the values?
Consider this data:
A
Combined all answers (and more) into a generic LINQ extension supporting caching which handles any data type, can be case-insensitive and allows to be chained with pre- and post-ordering:
public static class SortBySample
{
public static BySampleSorter Create(IEnumerable fixedOrder, IEqualityComparer comparer = null)
{
return new BySampleSorter(fixedOrder, comparer);
}
public static BySampleSorter Create(IEqualityComparer comparer, params TKey[] fixedOrder)
{
return new BySampleSorter(fixedOrder, comparer);
}
public static IOrderedEnumerable OrderBySample(this IEnumerable source, Func keySelector, BySampleSorter sample)
{
return sample.OrderBySample(source, keySelector);
}
public static IOrderedEnumerable ThenBySample(this IOrderedEnumerable source, Func keySelector, BySampleSorter sample)
{
return sample.ThenBySample(source, keySelector);
}
}
public class BySampleSorter
{
private readonly Dictionary dict;
public BySampleSorter(IEnumerable fixedOrder, IEqualityComparer comparer = null)
{
this.dict = fixedOrder
.Select((key, index) => new KeyValuePair(key, index))
.ToDictionary(kv => kv.Key, kv => kv.Value, comparer ?? EqualityComparer.Default);
}
public BySampleSorter(IEqualityComparer comparer, params TKey[] fixedOrder)
: this(fixedOrder, comparer)
{
}
public IOrderedEnumerable OrderBySample(IEnumerable source, Func keySelector)
{
return source.OrderBy(item => this.GetOrderKey(keySelector(item)));
}
public IOrderedEnumerable ThenBySample(IOrderedEnumerable source, Func keySelector)
{
return source.CreateOrderedEnumerable(item => this.GetOrderKey(keySelector(item)), Comparer.Default, false);
}
private int GetOrderKey(TKey key)
{
int index;
return dict.TryGetValue(key, out index) ? index : int.MaxValue;
}
}
Sample usage using LINQPad-Dump():
var sample = SortBySample.Create(StringComparer.OrdinalIgnoreCase, "one", "two", "three", "four");
var unsorted = new[] {"seven", "six", "five", "four", "THREE", "tWo", "One", "zero"};
unsorted
.OrderBySample(x => x, sample)
.ThenBy(x => x)
.Dump("sorted by sample then by content");
unsorted
.OrderBy(x => x.Length)
.ThenBySample(x => x, sample)
.Dump("sorted by length then by sample");