Digambar
Digambar

Reputation: 73

How can I sort a list of numerical strings? Preferably using LINQ

I have a string in below manner:-

string[] things = new string[] { "1", "10", "2", "1_1", "2_1","3_1" };

The desired output is:

"1",
"1_1",
"2",
"2_1",
"3_1",
"10",

How I can achieve this using LINQ?

Upvotes: 3

Views: 385

Answers (5)

Mong Zhu
Mong Zhu

Reputation: 23732

Yes there is, you can split each part by _ and convert the first string part to an integer. When sorting afterwards this will ensure that 10 is not before 2. Then in the second step you order it according to the last number

string[] things = new string[] { "5_3", "5_2", "1", "10", "2", "1_1", "2_1", "1_2", "3_1" };

string[] ordered = things.OrderBy(x=>Convert.ToInt32(x.Split('_').First())).                       
                           ThenBy(x=>Convert.ToInt32(x.Split('_').Last())).ToArray();

Output:

enter image description here

EDIT: Here is the link to the documentation of ThenBy for the sake of informativity ;)

Upvotes: 4

Tim Schmelter
Tim Schmelter

Reputation: 460278

If that format is strict you could parse to Version and order by that:

string[] orderedThings = things
   .Select(t => new { Thing = t, Numbers = (t + "_0").Split('_') })
   .Where(x => x.Numbers.All(s => s.All(char.IsDigit)))
   .Select(x => new { x.Thing, VersionStr = String.Join(".",x.Numbers.Take(4)) })
   .OrderBy(x => new Version(x.VersionStr))
   .ThenBy(x => x.Thing)
   .Select(x => x.Thing)
   .ToArray();

The t + "_0" trick was necessary to ensure that also single digits can be parsed. A version needs at least a major and aminor part. This "works" also if there are more than 4 tokens(major, minor, build, and revision). Then only the first 4 are taken to initialize the Version instance.

Upvotes: 1

Code Pope
Code Pope

Reputation: 5459

This will give you the desired output:

string[] things = new string[] { "1", "10", "2", "1_1", "2_1", "3_1" };
var list =  from row in things select Decimal.Parse(row.Replace("_",CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator));
list = list.OrderBy(d=> d).ToList();
var StringList = from row in list select row.ToString("0.#").Replace(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, "_");

Upvotes: 2

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131712

You can use one of the overloads of OrderBy that allows you to create your own key and maybe specify your own comparer. It looks like you want to treat _ as a decimal separator.

One quick & dirty way would be to replace _ with the decimal separator, eg :

var decSeparator=CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;

var orderedThings=things.OrderBy(thing=>double.Parse( thing.Replace("_",decSeparator)) )
                        .ToArray();

This is quick & dirty because it assumes _ appears only once. If _ appeared multiple times, the replaced string wouldn't be a valid number

Upvotes: 2

SuperJMN
SuperJMN

Reputation: 14002

What about this?

string[] strings = new string[] { "1", "10", "2", "1_1", "2_1", "3_1" };

var ordered = from str in strings
                let weight = double.Parse(str.Replace("_", CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator))
                let pair = new { Weight = weight, OriginalString = str }
                orderby pair.Weight
                select pair.OriginalString;

Upvotes: 2

Related Questions