Reputation: 73
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
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:
EDIT: Here is the link to the documentation of ThenBy for the sake of informativity ;)
Upvotes: 4
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
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
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
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