-2

I want to order elements in a list based on if they are similar (based on string.Contains) to elements in another list, and then by a default order which is descending string ordinal order.

Given the following data:

var things = new[] {"_1", "_2", "_3", "_4"};
var preferred = new[] { "4", "2" };

Desired result:

1. _4
2. _2
3. _3
4. _1

Linq is preferred but not essential; readability is essential.

My existing solution works, but I don't think is very clear to the reader:

.OrderBy(theString =>
{
    for (var i = 0; i < preferred.Length; ++i)
    {
        if (theString.Contains(preferred[i], StringComparison.OrdinalIgnoreCase))
        {
            return i;
        }
    }
    return int.MaxValue;
})
.ThenByDescending(x => x.ToLower());
Ian Newson
  • 7,679
  • 2
  • 47
  • 80
  • 1
    see https://stackoverflow.com/questions/15275269/sort-a-list-from-another-list-ids/15275682 – Meysam Asadi Jul 26 '21 at 22:09
  • this kind of thing is always hard to make it obvious what it's doing, but you can certainly make it more linq like with ```var ordered = things.OrderBy(theString => preferred.Where(s => theString.Contains(s, StringComparison.OrdinalIgnoreCase)).Select((s, i) => i).DefaultIfEmpty(int.MaxValue).FirstOrDefault()).ThenByDescending(x => x.ToLower());``` – Keith Nicholas Jul 26 '21 at 22:30

1 Answers1

1

You could consider extracting the position-finder algorithm in a local function, but other than that I don't think there are much that you can do:

IOrderedEnumerable<string> ordered = things
    .OrderBy(x => PositionInPreferred(x))
    .ThenByDescending(x => x, StringComparer.OrdinalIgnoreCase);

int PositionInPreferred(string value)
{
    int index = Array.FindIndex(preferred,
        x => value.Contains(x, StringComparison.OrdinalIgnoreCase));
    return index == -1 ? int.MaxValue : index;
}

Whether this refactoring improves the readability of the code is debatable.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104