c # linq порядковые числа, которые являются строковыми (и вы не можете преобразовать их в int) - PullRequest
85 голосов
/ 18 июня 2011

Я пытаюсь отсортировать массив чисел, которые являются строками, и я хотел бы, чтобы они сортировались численно.

Подвох в том, что Я не могу преобразовать числа в 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, Боб, Лорен, Пол

Ответы [ 19 ]

93 голосов
/ 18 июня 2011

Передать пользовательский компаратор в 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;
        }
    }
}
79 голосов
/ 18 июня 2011

Просто блокнот с нулями одинаковой длины:

int maxlen = sizes.Max(x => x.Length);
sizes.OrderBy(x => x.PadLeft(maxlen, '0'));
67 голосов
/ 18 июня 2011

А как насчет этого ...

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);
}
47 голосов
/ 12 августа 2015

Значение представляет собой строку

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

Работает

6 голосов
/ 03 декабря 2016

В 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 sort

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
4 голосов
/ 15 мая 2014

Вы говорите, что не можете преобразовать числа в 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, боб, Лорен, Пол

4 голосов
/ 25 октября 2015

Полагаю, это будет намного лучше, если в строке есть числовое значение.Надеюсь, что это поможет.

PS: я не уверен насчет производительности или сложных строковых значений, но это сработало примерно так:

lorem ipsum
lorem ipsum 1
loremipsum 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);
    }
}
4 голосов
/ 18 июня 2011

попробуйте

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

Примечание: это будет полезно, когда все строки преобразуются в int .....

3 голосов
/ 18 июня 2011

Это кажется странным запросом и заслуживает странного решения:

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);
}
2 голосов
/ 26 ноября 2014

Ответ, данный Джеффом Полсеном, является правильным, но 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, это если результат больше, меньше или равен нулю. Можно просто вычесть значения из другого и не обрабатывать возвращаемые значения.

Также метод IsNumeric не должен использовать try -блок и может извлечь выгоду из TryParse.

А для тех, кто не уверен: Этот Comparer будет сортировать значения так, чтобы нечисловые значения всегда добавлялись в конец списка. Если кто-то хочет их в начале, то следует поменять местами второй и третий if блок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...