orderby multiple framework convert columns c# linq

framework - order by linq c# multiple columns



c#linq orderby numeros que son cadenas(y no puedes convertirlos a int) (17)

Aunque esta es una vieja pregunta, me gustaría dar una solución:

string[] things= new string[] { "105", "101", "102", "103", "90" }; foreach (var thing in things.OrderBy(x => Int32.Parse(x) ) { Console.WriteLine(thing); }

Woha bastante simple, ¿verdad? :RE

Estoy tratando de ordenar una matriz de números que son cadenas y me gustaría que clasifiquen numéricamente.

La trampa es que no puedo convertir los números en int .

Aquí está el código:

string[] things= new string[] { "105", "101", "102", "103", "90" }; foreach (var thing in things.OrderBy(x => x)) { Console.WriteLine(thing); }

salida: 101, 102, 103, 105, 90

Me gustaría: 90, 101, 102, 103, 105

EDITAR: La salida no puede ser 090, 101, 102 ...

Se actualizó la muestra del código para decir "cosas" en lugar de "tamaños". La matriz puede ser algo como esto:

string[] things= new string[] { "paul", "bob", "lauren", "007", "90" };

Eso significa que debe ordenarse alfabéticamente y por número:

007, 90, bob, lauren, paul


Este sitio analiza la clasificación alfanumérica y clasificará los números en un sentido lógico en lugar de hacerlo en sentido ASCII. También tiene en cuenta los alfas que lo rodean:

http://www.dotnetperls.com/alphanumeric-sorting

EJEMPLO:

  • C: /TestB/333.jpg
  • 11
  • C: /TestB/33.jpg
  • 1
  • C: /TestA/111.jpg
  • 111F
  • C: /TestA/11.jpg
  • 2
  • C: /TestA/1.jpg
  • 111D
  • 22
  • 111Z
  • C: /TestB/03.jpg
  • 1
  • 2
  • 11
  • 22
  • 111D
  • 111F
  • 111Z
  • C: /TestA/1.jpg
  • C: /TestA/11.jpg
  • C: /TestA/111.jpg
  • C: /TestB/03.jpg
  • C: /TestB/33.jpg
  • C: /TestB/333.jpg

El código es el siguiente:

class Program { static void Main(string[] args) { var arr = new string[] { "C:/TestB/333.jpg", "11", "C:/TestB/33.jpg", "1", "C:/TestA/111.jpg", "111F", "C:/TestA/11.jpg", "2", "C:/TestA/1.jpg", "111D", "22", "111Z", "C:/TestB/03.jpg" }; Array.Sort(arr, new AlphaNumericComparer()); foreach(var e in arr) { Console.WriteLine(e); } } } public class AlphaNumericComparer : IComparer { public int Compare(object x, object y) { string s1 = x as string; if (s1 == null) { return 0; } string s2 = y as string; if (s2 == null) { return 0; } int len1 = s1.Length; int len2 = s2.Length; int marker1 = 0; int marker2 = 0; // Walk through two the strings with two markers. while (marker1 < len1 && marker2 < len2) { char ch1 = s1[marker1]; char ch2 = s2[marker2]; // Some buffers we can build up characters in for each chunk. char[] space1 = new char[len1]; int loc1 = 0; char[] space2 = new char[len2]; int loc2 = 0; // Walk through all following characters that are digits or // characters in BOTH strings starting at the appropriate marker. // Collect char arrays. do { space1[loc1++] = ch1; marker1++; if (marker1 < len1) { ch1 = s1[marker1]; } else { break; } } while (char.IsDigit(ch1) == char.IsDigit(space1[0])); do { space2[loc2++] = ch2; marker2++; if (marker2 < len2) { ch2 = s2[marker2]; } else { break; } } while (char.IsDigit(ch2) == char.IsDigit(space2[0])); // If we have collected numbers, compare them numerically. // Otherwise, if we have strings, compare them alphabetically. string str1 = new string(space1); string str2 = new string(space2); int result; if (char.IsDigit(space1[0]) && char.IsDigit(space2[0])) { int thisNumericChunk = int.Parse(str1); int thatNumericChunk = int.Parse(str2); result = thisNumericChunk.CompareTo(thatNumericChunk); } else { result = str1.CompareTo(str2); } if (result != 0) { return result; } } return len1 - len2; } }


Esto parece una petición extraña y merece una solución extraña:

string[] sizes = new string[] { "105", "101", "102", "103", "90" }; foreach (var size in sizes.OrderBy(x => { double sum = 0; int position = 0; foreach (char c in x.ToCharArray().Reverse()) { sum += (c - 48) * (int)(Math.Pow(10,position)); position++; } return sum; })) { Console.WriteLine(size); }


Hay una función nativa en Windows StrCmpLogicalW que comparará los números de cadenas como números en lugar de letras. Es fácil hacer un comparador que llame a esa función y la use para sus comparaciones.

public class StrCmpLogicalComparer : Comparer<string> { [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)] private static extern int StrCmpLogicalW(string x, string y); public override int Compare(string x, string y) { return StrCmpLogicalW(x, y); } }

Incluso funciona en cadenas que tienen texto y números. Aquí hay un programa de ejemplo que mostrará la diferencia entre el ordenamiento predeterminado y el género StrCmpLogicalW

class Program { static void Main() { List<string> items = new List<string>() { "Example1.txt", "Example2.txt", "Example3.txt", "Example4.txt", "Example5.txt", "Example6.txt", "Example7.txt", "Example8.txt", "Example9.txt", "Example10.txt", "Example11.txt", "Example12.txt", "Example13.txt", "Example14.txt", "Example15.txt", "Example16.txt", "Example17.txt", "Example18.txt", "Example19.txt", "Example20.txt" }; items.Sort(); foreach (var item in items) { Console.WriteLine(item); } Console.WriteLine(); items.Sort(new StrCmpLogicalComparer()); foreach (var item in items) { Console.WriteLine(item); } Console.ReadLine(); } }

que salidas

Example1.txt Example10.txt Example11.txt Example12.txt Example13.txt Example14.txt Example15.txt Example16.txt Example17.txt Example18.txt Example19.txt Example2.txt Example20.txt Example3.txt Example4.txt Example5.txt Example6.txt Example7.txt Example8.txt Example9.txt Example1.txt Example2.txt Example3.txt Example4.txt Example5.txt Example6.txt Example7.txt Example8.txt Example9.txt Example10.txt Example11.txt Example12.txt Example13.txt Example14.txt Example15.txt Example16.txt Example17.txt Example18.txt Example19.txt Example20.txt


La respuesta dada por Jeff Paulsen es correcta, pero el Comprarer puede simplificarse mucho a esto:

public class SemiNumericComparer: IComparer<string> { public int Compare(string s1, string s2) { if (IsNumeric(s1) && IsNumeric(s2)) return Convert.ToInt32(s1) - Convert.ToInt32(s2) if (IsNumeric(s1) && !IsNumeric(s2)) return -1; if (!IsNumeric(s1) && IsNumeric(s2)) return 1; return string.Compare(s1, s2, true); } public static bool IsNumeric(object value) { int result; return Int32.TryParse(value, out result); } }

Esto funciona porque lo único que se verifica para el resultado del Comparer es si el resultado es mayor, menor o igual a cero. Uno puede simplemente restar los valores de otro y no tiene que manejar los valores devueltos.

Además, el método IsNumeric no debería tener que usar un try -block y puede beneficiarse de TryParse .

Y para aquellos que no están seguros: Este Comparer clasificará los valores de modo que los valores no numéricos siempre se anexen al final de la lista. Si uno los quiere al principio, el segundo y el tercer bloque deben intercambiarse.


Mi solución preferida (si todas las cadenas son numéricas solamente):

// Order by numerical order: (Assertion: all things are numeric strings only) foreach (var thing in things.OrderBy(int.Parse)) { Console.Writeline(thing); }


Pase un comparador personalizado a OrderBy. Enumerable.OrderBy le permitirá especificar el comparador que desee.

Esta es una forma de hacer eso:

void Main() { string[] things= new string[] { "paul", "bob", "lauren", "007", "90", "101"}; foreach (var thing in things.OrderBy(x => x, new SemiNumericComparer())) { Console.WriteLine(thing); } } public class SemiNumericComparer: IComparer<string> { public int Compare(string s1, string s2) { if (IsNumeric(s1) && IsNumeric(s2)) { if (Convert.ToInt32(s1) > Convert.ToInt32(s2)) return 1; if (Convert.ToInt32(s1) < Convert.ToInt32(s2)) return -1; if (Convert.ToInt32(s1) == Convert.ToInt32(s2)) return 0; } if (IsNumeric(s1) && !IsNumeric(s2)) return -1; if (!IsNumeric(s1) && IsNumeric(s2)) return 1; return string.Compare(s1, s2, true); } public static bool IsNumeric(object value) { try { int i = Convert.ToInt32(value.ToString()); return true; } catch (FormatException) { return false; } } }


Prueba esto :

string[] things= new string[] { "105", "101", "102", "103", "90" }; int tmpNumber; foreach (var thing in (things.Where(xx => int.TryParse(xx, out tmpNumber)).OrderBy(xx => int.Parse(xx))).Concat(things.Where(xx => !int.TryParse(xx, out tmpNumber)).OrderBy(xx => xx))) { Console.WriteLine(thing); }


Simplemente rellene con ceros con la misma longitud:

int maxlen = sizes.Max(x => x.Length); sizes.OrderBy(x => x.PadLeft(maxlen, ''0''));


Supongo que esto será mucho mejor si tiene algún número en la cadena. Espero que ayude

PD: No estoy seguro sobre el rendimiento o los valores de cadena complicados, pero funcionó bien algo como esto:

lorem ipsum
lorem ipsum 1
lorem ipsum 2
lorem ipsum 3
...
lorem ipsum 20
lorem ipsum 21

public class SemiNumericComparer : IComparer<string> { public int Compare(string s1, string s2) { int s1r, s2r; var s1n = IsNumeric(s1, out s1r); var s2n = IsNumeric(s2, out s2r); if (s1n && s2n) return s1r - s2r; else if (s1n) return -1; else if (s2n) return 1; var num1 = Regex.Match(s1, @"/d+$"); var num2 = Regex.Match(s2, @"/d+$"); var onlyString1 = s1.Remove(num1.Index, num1.Length); var onlyString2 = s2.Remove(num2.Index, num2.Length); if (onlyString1 == onlyString2) { if (num1.Success && num2.Success) return Convert.ToInt32(num1.Value) - Convert.ToInt32(num2.Value); else if (num1.Success) return 1; else if (num2.Success) return -1; } return string.Compare(s1, s2, true); } public bool IsNumeric(string value, out int result) { return int.TryParse(value, out result); } }


Usted dice que no puede convertir los números en int porque la matriz puede contener elementos que no se pueden convertir a int, pero no hay ningún problema al intentar:

string[] things = new string[] { "105", "101", "102", "103", "90", "paul", "bob", "lauren", "007", "90" }; Array.Sort(things, CompareThings); foreach (var thing in things) Debug.WriteLine(thing);

Entonces compare así:

private static int CompareThings(string x, string y) { int intX, intY; if (int.TryParse(x, out intX) && int.TryParse(y, out intY)) return intX.CompareTo(intY); return x.CompareTo(y); }

Salida: 007, 90, 90, 101, 102, 103, 105, bob, lauren, paul


Y, qué tal esto ...

string[] sizes = new string[] { "105", "101", "102", "103", "90" }; var size = from x in sizes orderby x.Length, x select x; foreach (var p in size) { Console.WriteLine(p); }


prueba esto

sizes.OrderBy(x => Convert.ToInt32(x)).ToList<string>();

Nota: esto será útil cuando todos sean cadenas convertibles a int .....


El valor es una cadena

List = List.OrderBy(c => c.Value.Length).ThenBy(c => c.Value).ToList();

Trabajos


Try this out.. string[] things = new string[] { "paul", "bob", "lauren", "007", "90", "-10" }; List<int> num = new List<int>(); List<string> str = new List<string>(); for (int i = 0; i < things.Count(); i++) { int result; if (int.TryParse(things[i], out result)) { num.Add(result); } else { str.Add(things[i]); } }

Ahora Ordene las listas y vuelva a fusionarlas ...

var strsort = from s in str orderby s.Length select s; var numsort = from n in num orderby n select n; for (int i = 0; i < things.Count(); i++) { if(i < numsort.Count()) things[i] = numsort.ElementAt(i).ToString(); else things[i] = strsort.ElementAt(i - numsort.Count()); }

Sin embargo, intenté hacer una contribución en esta interesante pregunta ...


public class NaturalSort: IComparer<string> { [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] public static extern int StrCmpLogicalW(string x, string y); public int Compare(string x, string y) { return StrCmpLogicalW(x, y); } }

arr = arr.OrderBy (x => x, nuevo NaturalSort ()). ToArray ();

La razón por la que lo necesitaba era archivar en un directorio cuyos nombres de archivo comenzaban con un número:

public static FileInfo[] GetFiles(string path) { return new DirectoryInfo(path).GetFiles() .OrderBy(x => x.Name, new NaturalSort()) .ToArray(); }


public class Test { public void TestMethod() { List<string> buyersList = new List<string>() { "5", "10", "1", "str", "3", "string" }; List<string> soretedBuyersList = null; soretedBuyersList = new List<string>(SortedList(buyersList)); } public List<string> SortedList(List<string> unsoredList) { return unsoredList.OrderBy(o => o, new SortNumericComparer()).ToList(); } } public class SortNumericComparer : IComparer<string> { public int Compare(string x, string y) { int xInt = 0; int yInt = 0; int result = -1; if (!int.TryParse(x, out xInt)) { result = 1; } if(int.TryParse(y, out yInt)) { if(result == -1) { result = xInt - yInt; } } else if(result == 1) { result = string.Compare(x, y, true); } return result; } }