c # linq orderby 숫자는 문자열이며 int로 변환 할 수 없습니다.
문자열 인 숫자 배열을 정렬하려고하는데 숫자로 정렬하고 싶습니다.
문제 는 숫자를 int로 변환 할 수 없다는 것 입니다.
다음은 코드입니다.
string[] things= new string[] { "105", "101", "102", "103", "90" };
foreach (var thing in things.OrderBy(x => x))
{
Console.WriteLine(thing);
}
출력 : 101, 102, 103, 105, 90
원하는 항목 : 90, 101, 102, 103, 105
편집 : 출력은 090, 101, 102 ... 일 수 없습니다.
"크기"대신 "사물"을 말하도록 코드 샘플을 업데이트했습니다. 배열은 다음과 같을 수 있습니다.
string[] things= new string[] { "paul", "bob", "lauren", "007", "90" };
즉, 알파벳순과 숫자 순으로 정렬해야합니다.
007, 90, 밥, 로렌, 폴
사용자 지정 비교자를 OrderBy에 전달합니다. Enumerable.OrderBy 를 사용하면 원하는 비교자를 지정할 수 있습니다.
이를 수행하는 한 가지 방법입니다.
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;
}
}
}
같은 길이로 0으로 채 웁니다.
int maxlen = sizes.Max(x => x.Length);
sizes.OrderBy(x => x.PadLeft(maxlen, '0'));
그리고 이것은 어떻습니까 ...
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);
}
값은 문자열입니다.
List = List.OrderBy(c => c.Value.Length).ThenBy(c => c.Value).ToList();
공장
Windows StrCmpLogicalW
에는 문자 대신 숫자로 문자열 숫자를 비교 하는 기본 함수가 있습니다 . 해당 함수를 호출하고 비교에 사용하는 비교자를 만드는 것은 쉽습니다.
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);
}
}
텍스트와 숫자가 모두있는 문자열에서도 작동합니다. 여기에 기본 정렬과 사이의 디퍼 런스 세라마이드 표시하는 예제 프로그램입니다 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();
}
}
어느 출력
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
이 시도
sizes.OrderBy(x => Convert.ToInt32(x)).ToList<string>();
참고 : 이것은 모든 문자열이 int .....로 변환 가능한 경우에 유용합니다.
배열에 int로 변환 할 수없는 요소가 포함될 수 있기 때문에 숫자를 int로 변환 할 수 없다고 말하지만 시도해도 아무런 해가 없습니다.
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);
그런 다음 다음과 같이 비교하십시오.
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);
}
출력 : 007, 90, 90, 101, 102, 103, 105, bob, lauren, paul
문자열에 숫자가 있으면 훨씬 더 좋을 것 같습니다. 도움이되기를 바랍니다.
추신 : 성능이나 복잡한 문자열 값에 대해서는 잘 모르겠지만 다음과 같이 잘 작동했습니다.
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);
}
}
이것은 이상한 요청으로 보이며 이상한 해결책이 필요합니다.
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);
}
이 사이트에서는 영숫자 정렬에 대해 설명하고 ASCII 의미가 아닌 논리적 의미로 숫자를 정렬합니다. 또한 주변의 알파도 고려합니다.
http://www.dotnetperls.com/alphanumeric-sorting
예:
- 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
코드는 다음과 같습니다.
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;
}
}
Jeff Paulsen이 제공 한 대답은 정확하지만 다음과 Comprarer
같이 훨씬 단순화 할 수 있습니다.
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);
}
}
결과를 확인하는 유일한 것은 Comparer
결과가 더 크거나 작거나 0인지 여부입니다. 하나는 단순히 다른 값에서 값을 뺄 수 있으며 반환 값을 처리 할 필요가 없습니다.
또한이 IsNumeric
메서드는 try
-block 을 사용할 필요가 없으며 TryParse
.
그리고 확실하지 않은 사람들을 위해 :이 비교기는 숫자가 아닌 값이 항상 목록 끝에 추가되도록 값을 정렬합니다. 처음에 원하는 경우 두 번째와 세 번째 if
블록을 교체해야합니다.
이 시도 :
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);
}
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, new NaturalSort ()). ToArray ();
내가 필요한 이유는 파일 이름이 숫자로 시작되는 디렉토리에 보관하기 위해서입니다.
public static FileInfo[] GetFiles(string path)
{
return new DirectoryInfo(path).GetFiles()
.OrderBy(x => x.Name, new NaturalSort())
.ToArray();
}
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]);
}
}
이제 목록을 정렬하고 다시 병합하십시오 ...
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());
}
나는이 흥미로운 질문에 기여하려고 노력했습니다 ...
내가 선호하는 솔루션 (모든 문자열이 숫자 전용 인 경우) :
// Order by numerical order: (Assertion: all things are numeric strings only)
foreach (var thing in things.OrderBy(int.Parse))
{
Console.Writeline(thing);
}
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;
}
}
Jeff Paulsen 답변을 확장합니다. 문자열에 몇 개의 숫자 또는 문자 그룹이 있는지는 중요하지 않은지 확인하고 싶었습니다.
public class SemiNumericComparer : IComparer<string>
{
public int Compare(string s1, string s2)
{
if (int.TryParse(s1, out var i1) && int.TryParse(s2, out var i2))
{
if (i1 > i2)
{
return 1;
}
if (i1 < i2)
{
return -1;
}
if (i1 == i2)
{
return 0;
}
}
var text1 = SplitCharsAndNums(s1);
var text2 = SplitCharsAndNums(s2);
if (text1.Length > 1 && text2.Length > 1)
{
for (var i = 0; i < Math.Max(text1.Length, text2.Length); i++)
{
if (text1[i] != null && text2[i] != null)
{
var pos = Compare(text1[i], text2[i]);
if (pos != 0)
{
return pos;
}
}
else
{
//text1[i] is null there for the string is shorter and comes before a longer string.
if (text1[i] == null)
{
return -1;
}
if (text2[i] == null)
{
return 1;
}
}
}
}
return string.Compare(s1, s2, true);
}
private string[] SplitCharsAndNums(string text)
{
var sb = new StringBuilder();
for (var i = 0; i < text.Length - 1; i++)
{
if ((!char.IsDigit(text[i]) && char.IsDigit(text[i + 1])) ||
(char.IsDigit(text[i]) && !char.IsDigit(text[i + 1])))
{
sb.Append(text[i]);
sb.Append(" ");
}
else
{
sb.Append(text[i]);
}
}
sb.Append(text[text.Length - 1]);
return sb.ToString().Split(' ');
}
}
또한 파일 이름을 처리하도록 수정 한 후 SO 페이지 에서 SplitCharsAndNums를 가져 왔습니다 .
이것은 오래된 질문이지만 해결책을 제시하고 싶습니다.
string[] things= new string[] { "105", "101", "102", "103", "90" };
foreach (var thing in things.OrderBy(x => Int32.Parse(x) )
{
Console.WriteLine(thing);
}
Woha 아주 간단 하죠? :디
namespace X
{
public class Utils
{
public class StrCmpLogicalComparer : IComparer<Projects.Sample>
{
[DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string x, string y);
public int Compare(Projects.Sample x, Projects.Sample y)
{
string[] ls1 = x.sample_name.Split("_");
string[] ls2 = y.sample_name.Split("_");
string s1 = ls1[0];
string s2 = ls2[0];
return StrCmpLogicalW(s1, s2);
}
}
}
}
'developer tip' 카테고리의 다른 글
PHP에서 sqlite3를 활성화하는 방법은 무엇입니까? (0) | 2020.09.06 |
---|---|
Azure에서 'Microsoft.SqlServer.Types'버전 10 이상을 찾을 수 없습니다. (0) | 2020.09.06 |
Nested Scrollview 스크롤 내부의 Recyclerview가 일반 Recyclerview 또는 Nested Scrollview처럼 빠르게 스크롤되지 않습니다. (0) | 2020.09.06 |
IntelliJ Gradle 플러그인 : 제공된 javaHome이 유효하지 않은 것 같습니다. (0) | 2020.09.06 |
Golang에서 http.Get () 요청에 대한 시간 제한을 설정하는 방법은 무엇입니까? (0) | 2020.09.06 |