Правильная сортировка строк, содержащих числа в начале

    При стандартной лексикографической сортировке строк, начинающихся с чисел, возникает проблема: система сравнивает их посимвольно, что приводит к некорректному порядку. Например, строки "1 строка", "2 строка", "11 строка" будут расположены как "1 строка", "11 строка", "2 строка", поскольку сравнение происходит по первому символу "1" и "2".

    Требуется реализовать сортировку, при которой:

    • Сначала извлекается и учитывается полное числовое значение в начале строки.
    • Если число отсутствует, сравнение происходит по алфавиту.
    • Ожидаемый результат: "1 строка", "2 строка", "11 строка".

    Проблема текущего решения

    Предложенный код использует сравнение только первых символов:

    usort($subcats, function ($a, $b) {
        return strcmp(mb_substr($a->name, 0, 1), mb_substr($b->name, 0, 1));
    });

    Этот подход не подходит, так как он анализирует исключительно первый символ, игнорируя многозначные числа.

    Универсальное решение

    Для корректной обработки необходимо реализовать функцию сравнения, которая:

    1. Извлекает числовой префикс из каждой строки.
    2. Сравнивает строки сначала по числовому значению (если оно присутствует в обеих).
    3. Если числа нет или они равны, переходит к лексикографическому сравнению.

    Вот пример реализации на PHP с использованием функции usort и пользовательского компаратора:

    usort($subcats, function ($a, $b) {
        // Извлечение чисел из начала строк
        preg_match('/^\d+/', $a->name, $matchesA);
        preg_match('/^\d+/', $b->name, $matchesB);
        
        $numA = $matchesA[0] ?? null;
        $numB = $matchesB[0] ?? null;
        
        // Если оба значения - числа, сравниваем их численно
        if ($numA !== null && $numB !== null) {
            if ($numA != $numB) {
                return $numA <=> $numB;
            }
            // Если числа равны, сравниваем полные строки лексикографически
            return strcmp($a->name, $b->name);
        }
        
        // Если только одно из значений - число, число должно идти первым
        if ($numA !== null && $numB === null) {
            return -1;
        }
        if ($numA === null && $numB !== null) {
            return 1;
        }
        
        // Если чисел нет в обеих строках, стандартное строковое сравнение
        return strcmp($a->name, $b->name);
    });

    Этот алгоритм обеспечивает естественную сортировку (natural sort), где числовые префиксы обрабатываются как целые числа, а не как последовательности символов.

    Альтернативные подходы

    • Использование функции natsort() или natcasesort() для массивов, если требуется сортировка по значениям.
    • Применение флага SORT_NATURAL в функциях типа sort() или asort().
    • Для более сложных сценариев (например, числа внутри текста) можно рассмотреть функцию strnatcmp().

    Представленное решение является универсальным и корректно обрабатывает смешанные данные, обеспечивая интуитивно понятный порядок сортировки.