2

How to use multiple sort_flags in PHP array sorting (Using SORT_LOCALE_STRING , SORT_NATURAL)?

I want to using SORT_LOCALE_STRING for UTF8 languages + SORT_NATURAL Numbers.


I want to Sort the following array:

$array=array('Alpha', 'Älpha1', 'Älpha2', 'Älpha10', 'Älpha3', 'Älpha4', 'Bravo');

My favorite result after sorting:

Array
(
    [0] => Alpha
    [1] => Älpha1
    [2] => Älpha2
    [3] => Älpha3
    [4] => Älpha4
    [5] => Älpha10
    [6] => Bravo
)

But when using SORT_LOCALE_STRING:

<?php
$array=array('Alpha', 'Älpha1', 'Älpha2', 'Älpha10', 'Älpha3', 'Älpha4', 'Bravo');
setlocale(LC_COLLATE, 'de_DE.UTF8', 'de.UTF8', 'de_DE.UTF-8', 'de.UTF-8');
sort($array, SORT_LOCALE_STRING);
print_r($array);
?>

Results:

Array
(
    [0] => Alpha
    [1] => Älpha1
    [2] => Älpha10
    [3] => Älpha2
    [4] => Älpha3
    [5] => Älpha4
    [6] => Bravo
)

AND when using SORT_NATURAL:

<?php
$array=array('Alpha', 'Älpha1', 'Älpha2', 'Älpha10', 'Älpha3', 'Älpha4', 'Bravo');
sort($array, SORT_NATURAL);
print_r($array);
?>

Results:

Array
(
    [0] => Alpha
    [1] => Bravo
    [2] => Älpha1
    [3] => Älpha2
    [4] => Älpha3
    [5] => Älpha4
    [6] => Älpha10
)

How can I get result like this?!

Array
(
    [0] => Alpha
    [1] => Älpha1
    [2] => Älpha2
    [3] => Älpha3
    [4] => Älpha4
    [5] => Älpha10
    [6] => Bravo
)

UPDATE:

I finally found the solution, By Using intl AND The Collator class.

First, enable PHP Extension intl.

Then:

<?php
$array=array('Alpha', 'Älpha1', 'Älpha2', 'Älpha10', 'Älpha3', 'Älpha4', 'Bravo');
$collator = new Collator('de_DE.UTF8');
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
$collator->setAttribute(Collator::CASE_FIRST, Collator::LOWER_FIRST);
$collator->asort($array);
print_r($array);
?>

Results:

Array
(
    [0] => Alpha
    [1] => Älpha1
    [2] => Älpha2
    [4] => Älpha3
    [5] => Älpha4
    [3] => Älpha10
    [6] => Bravo
)
Alex
  • 299
  • 1
  • 8

2 Answers2

1

The issue is that the two flags you want to use almost contradict each other. SORT_NATURAL wants to treat the value as a mixture of a string and numeric, whereas SORT_LOCALE_STRING wants to treat it purely as a string.

Not sure if there is a simpler way, but this code translates all the letters into a standard alphabet (using iconv()), sorts this array (using asort to keep the keys and SORT_NATURAL) and then substitutes back the original string (using array_replace())...

$array=array('Alpha', 'Älpha1', 'Älpha2', 'Älpha10', 'Älpha3', 'Älpha4', 'Bravo');
setlocale(LC_ALL, "en_US.utf8");
$trans = $array;
array_walk($trans, function (&$data) {
    $data =  iconv("UTF-8", 'ASCII//TRANSLIT//IGNORE', $data);
});
asort($trans, SORT_NATURAL);
$array = array_replace($trans, $array);
print_r($array);

gives...

Array
(
    [0] => Alpha
    [1] => Älpha1
    [2] => Älpha2
    [4] => Älpha3
    [5] => Älpha4
    [3] => Älpha10
    [6] => Bravo
)
Nigel Ren
  • 56,122
  • 11
  • 43
  • 55
  • Thanks for the reply. But your solution is not working properly in some languages, such as Arabic. Please see the solution in the First post. – Alex Oct 29 '19 at 07:05
  • It's difficult to know all of the classes or possibilities, but at least you found what you are after. I would suggest posting that as an answer, and you can (eventually) choose it as the accepted answer. – Nigel Ren Oct 29 '19 at 07:21
1

First, enable PHP Extension intl.

Then:

<?php
$array=array('Alpha', 'Älpha1', 'Älpha2', 'Älpha10', 'Älpha3', 'Älpha4', 'Bravo');
$collator = new Collator('de_DE.UTF8');
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
$collator->setAttribute(Collator::CASE_FIRST, Collator::LOWER_FIRST);
$collator->asort($array);
print_r($array);
?>

Results:

Array
(
    [0] => Alpha
    [1] => Älpha1
    [2] => Älpha2
    [4] => Älpha3
    [5] => Älpha4
    [3] => Älpha10
    [6] => Bravo
)
Alex
  • 299
  • 1
  • 8