0

I have list which I want to order by like this

  • First by "ab"
  • Then by alphabetical order inside the list by "ab"
  • Then by "cd"
  • Then by alphabetical order inside the list by "cd"
  • Then by "ef"
  • Then by alphabetical order inside the list by "ef"

and then the rest by alphabetical order

I have this linq query

var groups =  profileModel.Groups.
OrderByDescending(i => i.FullName.ToLower().Contains("ab")).
ThenByDescending(i => i.FullName.ToLower().Contains("cd")).
ThenByDescending(i => i.FullName.ToLower().Contains("ef"));

How should I extend this one? Do I have to use group by?

mohsinali1317
  • 4,255
  • 9
  • 46
  • 85

4 Answers4

0

It seems that you want this, then no need to use GroupBy:

var groupOrders = new List<string> { "ab", "cd", "ef" };

var resultList = profileModel.Groups
    .Select(x => new { ModelGroup = x, First2Letter = x.FullName.Substring(Math.Min(2, x.FullName.Length)) })
    .Select(x => new
    {
        x.ModelGroup,
        x.First2Letter,
        Index = groupOrders.FindIndex(s => s.Equals(x.First2Letter, StringComparison.OrdinalIgnoreCase))
    })
    .OrderByDescending(x => x.Index >= 0) // first all known groups
    .ThenBy(x => x.Index)
    .ThenBy(x => x.ModelGroup.FullName)
    .Select(x => x.ModelGroup)
    .ToList();
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
0

If I got the problem correctly, this will order items based on what they contain. should work in EF as well.

var orderItems = from item in profileModel.Groups
                 let name = item.FullName.ToLower()
                 orderby (name.Contains("ab") ? 1 : 0) + (name.Contains("cd") ? 0.1 : 0) + (name.Contains("ef") ? 0.01 : 0) descending
                 select item;

EDIT After rereading the problem this might be the right solution

var orderItems = from item in profileModel.Groups
                 let name = item.FullName.ToLower()
                 let order = name.Contains("ab") ? 3 : name.Contains("cd") ? 2 : name.Contains("ef") ? 1 : 0
                 orderby order descending, item.FullName
                 select item;
Filip Cordas
  • 2,531
  • 1
  • 12
  • 23
0

For custom ordering, you can assign a value to each compare condition and OrderByDescending will order by that. Something like this..

            lstModel = profileModel.Groups.OrderByDescending(m => m.FullName.ToLower().Contains("ab") ? 3 :
            m.FullName.ToLower().Contains("cd") ? 2 :
            m.FullName.ToLower().Contains("ef") ? 1 : 0).ToList();
Obaid
  • 2,563
  • 17
  • 15
0

If you might have more or different level1 values to test, you may want a generic version.

Using a convenient extension method FirstOrDefault that takes the default value to return

public static T FirstOrDefault<T>(this IEnumerable<T> src, Func<T, bool> test, T defval) => src.Where(aT => test(aT)).DefaultIfEmpty(defval).First();

You can create an array of first level values in order, and sort first on that, then alphabetically:

var level1 = new[] { "ab", "cd", "ef" };

var ans = groups.OrderBy(i => level1.Select((op, n) => (op, n))
                                    .FirstOrDefault(opn => i.FullName.Contains(opn.op),
                                                    (op: String.Empty, n: level1.Length)).n)
                .ThenBy(i => i.FullName);
NetMage
  • 26,163
  • 3
  • 34
  • 55