Como posso classificar uma seqüência de texto seguida por um número usando LINQ

Eu tenho usado o seguinte tipo:

var query = _cityRepository.GetAll( .OrderBy(item => item.RowKey.Substring(0, 4)) .ThenBy(item => item.ShortTitle) 

No entanto estou tendo um problema porque meu ShortTitle se parece com isso:

 Liverpool - 1 Liverpool - 2 ... Liverpool - 9 Liverpool - 10 Liverpool - 11 West Kirby - 1 West Kirby - 8 West Kirby - 12 

Quando eu classifico isso usando o LINQ, ele vem na ordem

 Liverpool - 1 Liverpool - 11 Liverpool - 12 Liverpool - 2 West Kirby - 1 West Kirby - 12 West Kirby - 8 

O ShortTitle é sempre uma sequência de palavras seguida por um único hífen e depois por um número.

Existe uma maneira que eu possa conseguir isso para classificar corretamente?

É porque você está classificando-os como strings, e para strings 11 vem antes de 2. Você precisará analisar o ShortTitle para obter o valor (supondo) int no final e ordenar por isso.

Sua consulta LINQ pode ser alterada para que isso funcione:

 var query = _cityRepository.GetAll( .OrderBy(item => item.RowKey.Substring(0, 4)) .ThenBy(item => item.ShortTitle.Split('-').First()) .ThenBy(item => Convert.ToInt16(item.ShortTitle.Split().Last())); 

Tente isso

 List list = new List() { "Liverpool - 1", "Liverpool - 11", "Liverpool - 12", "Liverpool - 2", "West Kirby - 1", "West Kirby - 12", "West Kirby - 8" }; var sortedList = list.CustomSort().ToArray(); public static class MyExtensions { public static IEnumerable CustomSort(this IEnumerable list) { int maxLen = list.Select(s => s.Length).Max(); return list.Select(s => new { OrgStr = s, SortStr = Regex.Replace(s, @"(\d+)|(\D+)", m => m.Value.PadLeft(maxLen, char.IsDigit(m.Value[0]) ? ' ' : '\xffff')) }) .OrderBy(x => x.SortStr) .Select(x => x.OrgStr); } } 

rapido e sujo:

 class Program { static void Main(string[] args) { var list = new[] {"Liverpool - 1", "Liverpool - 11", "Liverpool - 123", "Liverpool - 342", "Liverpool - 2"}; foreach (var x in list.OrderBy(s => Int32.Parse(Regex.Match(s, @"- (\d*)").Groups[1].Value))) Console.WriteLine(x); } } 

Tarde para a festa, mas eu não vou deixar isso me parar!

Piggybacking na boa solução do @ NominSim:

 var query = _cityRepository.GetAll() .Select(item => { var fields = item.ShortTitle.Split('-'); return new { Key = item.RowKey.Substring(0,4), Title = fields[0].Trim(), Index = Convert.ToInt16(fields[1]) } }) .OrderBy(item => item.Key) .ThenBy(item => item.Title) .ThenBy(item => item.Index); 

Realmente, a principal diferença é capturar o resultado da divisão para que não precisemos fazer a divisão novamente para obter o índice e, em seguida, adicionamos um Trim () ao título.

Você pode criar seu próprio Comparador e passá-lo para OrderBy ou ThenBy para ordená-los da maneira que você quiser.

Por exemplo, este comparador muito primitivo, irá preencher o último número com 0s antes de converter as strings:

 class MyComparer:Comparer { public override int Compare(string x, string y) { var xL = xparts[xparts.Length - 1]; long xN; if (long.TryParse(xL,out xN)) { xparts[xparts.Length - 1] = xN.ToString().PadLeft(20,'0'); } var yL = yparts[yparts.Length - 1]; long yN; if (long.TryParse(yL, out yN)) { yparts[yparts.Length - 1] = yN.ToString().PadLeft(20, '0'); } var x2=String.Join(" ", xparts); var y2 = String.Join(" ", yparts); return x2.CompareTo(y2); } } 

e você pode chamá-lo com alterações mínimas:

 var query = _cityRepository.GetAll( .OrderBy(item => item.RowKey.Substring(0, 4)) .ThenBy(item => item.ShortTitle, new MyComparer());