Reputation: 90
I have a following string list:
List<string> a = new List<string>();
a.Add("2015");
a.Add("2015 /M1");
a.Add("2015 /M2");
a.Add("2015 /M9");
a.Add("2015 /M10");
a.Add("2015 /M11");
a.Add("2015 /M12");
a.Add("2015 /M3");
a.Add("2015 /M4");
a.Add("2015 /M5");
a.Add("2015 /M6");
a.Add("2015 /M7");
a.Add("2015 /M8");
When I call the sort function a.sort()
it sorts like this:
2015
2015 /M1
2015 /M11
2015 /M12
2015 /M2
2015 /M3
2015 /M4
2015 /M5
2015 /M6
2015 /M7
2015 /M8
2015 /M9
But how can I modify to make it look like below.
2015
2015 /M1
2015 /M2
2015 /M3
2015 /M4
2015 /M5
2015 /M6
2015 /M7
2015 /M8
2015 /M9
2015 /M10
2015 /M11
2015 /M12
I have same pattern in other list items as well like 2015 Q/12, 2015 Q/11 etc.
Upvotes: 1
Views: 239
Reputation: 5566
You are looking for natural sort order. In the linked question you can find a pure LINQ implementation which can be reused for any natural sort order problem.
Upvotes: 1
Reputation: 7672
You need to extract the number from string, it can be done by regex. Then convert it to integer and sort by it.
var e = from s in a
let g = Regex.Match(s, @"^\d+(?: \/[MQ](\d+))?$")
let n = g.Groups[1].Value != "" ? int.Parse(g.Groups[1].Value) : (int?)null
orderby n
select s;
a = e.ToList();
To sort by year first, then use following code
var e = from s in a
let g = Regex.Match(s, @"^[A-Za-z]*(\d+)(?: \/[MQ](\d+))?$")
let y = g.Groups[1].Value != "" ? int.Parse(g.Groups[1].Value) : 0
let m = g.Groups[2].Value != "" ? int.Parse(g.Groups[2].Value) : 0
orderby y, m
select s;
Upvotes: 2
Reputation:
I have elaborated on Niyoko Yuliawans solution and the result is:
class StringComparer : IComparer<string>
{
const string pattern = @"^\D*(?<year>\d{4})( \/[MQ](?<index>\d+))?$";
public int Compare(string x, string y)
{
var mx = Regex.Match(x, pattern);
var my = Regex.Match(y, pattern);
int ix;
if (int.TryParse(mx.Groups["index"].Value, out ix))
{
int iy;
if (int.TryParse(my.Groups["index"].Value, out iy))
{
return ix.CompareTo(iy);
}
}
return mx.Groups["year"].Value.CompareTo(my.Groups["year"].Value);
}
}
class Program
{
static void Main(string[] args)
{
List<string> a = new List<string>();
a.Add("2015");
a.Add("2015 /Q1");
a.Add("CY2015 /Q2");
a.Add("2015 /Q9");
a.Add("2015 /Q10");
a.Add("2015 /Q11");
a.Add("2015 /Q12");
a.Add("2015 /Q3");
a.Add("2014");
a.Add("2015 /Q4");
a.Add("2015 /Q5");
a.Add("2015 /Q6");
a.Add("2015 /Q7");
a.Add("2015 /Q8");
a.Sort(new StringComparer());
foreach (var x in a)
{
Console.WriteLine(x);
}
Console.WriteLine("END");
Console.ReadLine();
}
}
It avoids the temporary sequence (e).
Upvotes: 0
Reputation: 117174
I did this:
var results =
a
.OrderBy(x => new string(x.Take(7).ToArray()))
.ThenBy(x => int.Parse(new string(x.Skip(7).DefaultIfEmpty('0').ToArray())));
...and got this:
2015 2015 /M1 2015 /M2 2015 /M3 2015 /M4 2015 /M5 2015 /M6 2015 /M7 2015 /M8 2015 /M9 2015 /M10 2015 /M11 2015 /M12
Upvotes: 1
Reputation: 557
This post was similar to yours. Edited as per your requirement:
var sortedList = a.OrderBy(x => PadNumbers(!x.Contains("M")? "" : x.Substring(x.IndexOf('M'), (x.Length - x.IndexOf('M'))))).ToList();
public static string PadNumbers(string input)
{
return Regex.Replace(input, "[0-9]+", match => match.Value.PadLeft(10, '0'));
}
Upvotes: 0
Reputation: 215
you can also write easily your own Extension for this sort function:
public static class Extension
{
public static List<string> sortItMyWay(this List<string> mylist)
{
string temp = string.Empty;
for (int write = 0; write < mylist.Count; write++)
{
for (int sort = 0; sort < mylist.Count - 1; sort++)
{
if (mylist[sort].Weight() > mylist[sort + 1].Weight())
{
temp = mylist[sort + 1];
mylist[sort + 1] = mylist[sort];
mylist[sort] = temp;
}
}
}
return mylist;
}
public static int Weight (this string input)
{
var value = 0;
for (int i = input.Length - 1; i >= 0 ; i--)
{
value += input[i] * (int)Math.Pow(10,i);
}
return value;
}
}
Upvotes: -1