yantrab
yantrab

Reputation: 2652

linq - list of string - order by letter only, and then by numbers

For example :

this array:

a 2
a 10
a

should be after sort :

a
a 2
a 10

I try this, but it doesn't not work: the order is wrong.

 ... 
.OrderBy(s => s.name)
.ThenBy(s => {
   var stringNumber = Regex.Match(s.name, @"\d+").Value;

   return string.IsNullOrEmpty(stringNumber) 
     ? 0 
     : int.Parse(stringNumber);
 });

Upvotes: 1

Views: 2890

Answers (4)

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186668

I suggest extracting criterium explicitly: name and number groups in the regular expression:

  var list = new List<string>() {
    "a",
    "a 4",
    "b 1",
    "a 2",
    "a 11"
  };

  var result = list
    .Select(item => new {
         value = item,
         match = Regex.Match(item, @"^(?<name>.*?)\s*(?<number>[0-9]*)$"), 
       })
    .OrderBy(item => item.match.Groups["name"].Value)
    .ThenBy(item => item.match.Groups["number"].Value.Length)
    .ThenBy(item => item.match.Groups["number"].Value)
    .Select(item => item.value);

Test:

Console.WriteLine(string.Join(Environment.NewLine, result));

Outcome:

a
a 2
a 4
a 11
b 1

Upvotes: 3

Emre Kabaoglu
Emre Kabaoglu

Reputation: 13146

You could try something like that;

        var list = new List<string>()
        {
            "a ",
            "a 4",
            "b 1",
            "a 2"
        };

        int value = 0;

        list = list
          .Select(x => new {
             WholeString = x,
             StringPart = x.Split(' ')[0],
             IntPart = int.TryParse(x.Split(' ')[1], out value) ? value : 0
            })
          .OrderBy(x => x.StringPart)
          .ThenBy(x => x.IntPart)
          .Select(x => x.WholeString)
          .ToList();

Upvotes: 1

spersson
spersson

Reputation: 550

var test = new[] {"a 2", "a 10", "a"};

var sorted = test.OrderBy(s => new string(s.Where(char.IsLetter).ToArray())).ThenBy(s =>
{
    var stringNumber = Regex.Match(s, @"\d+").Value;
    return string.IsNullOrEmpty(stringNumber) ? 0 : int.Parse(stringNumber);
});

This should sort by letters first, then by numbers.

Upvotes: 0

Ren&#233; Vogt
Ren&#233; Vogt

Reputation: 43876

Since you said "letter" I assume you want to only sort by the first character, then by the number.

So don't OrderBy the whole string, but by the letter only:

var result = list
          .OrderBy(s => s.FirstOrDefault()) // only first character
          .ThenBy(s =>
             {
                var stringNumber = Regex.Match(s.name, @"\d+").Value;
                return string.IsNullOrEmpty(stringNumber) ? 0 : int.Parse(stringNumber);
             })

Your code sorted by the whole string at first, so ThenBy had no effect anymore.

Upvotes: 2

Related Questions