PHP - a more efficient method for sorting alphanumeric keys

NOTE. I edited this example and replaced all values โ€‹โ€‹with 1. The values โ€‹โ€‹don't matter, just the keys. The previous values โ€‹โ€‹that I wrote have led to misunderstandings. Excuse me.

NOTE. A / b blocks are always contiguous. I am adding this because it was not clear. If there are 3a / 3b and 5a / 5b, there will always be 4a / 4b and not only 4.

I have arrays containing numbered keys with leading zeros. Sometimes these numbered keys have two variants, which I distinguish using the suffixes a and b. The number of keys with or without options is unknown, but no more than two digits; that is, the highest numeric key is "09".

The problem is that these array keys must be sorted numerically, but when suffixes are present, they must take precedence. Using ksort () alone does not achieve this.

For example, ksort () gives me this:

$arr = array(
    '01' => 1,
    '02' => 1,
    '03a' => 1,
    '03b' => 1,
    '04a' => 1,
    '04b' => 1,
    '05a' => 1,
    '05b' => 1,
    '06' => 1,
);

      

But I need this:

$arr = array(
    '01' => 1,
    '02' => 1,
    '03a' => 1,
    '04a' => 1,
    '05a' => 1,
    '03b' => 1,
    '04b' => 1,
    '05b' => 1,
    '06' => 1,
);

      


I'm using some fancy coding gymnastics to get what I want, but it's not pretty. I'm wondering if there is a better, cleaner way?


That's what I'm doing.

1) I am using ksort () which gives me the first of the two arrays above. (The one I don't want yet.)

2) I am creating two arrays. One for the 'a' suffixes, one for the 'b' suffixes.

$arr_a = array();
$arr_b = array();

foreach ($arr as $k => $v) {
    if (substr($k, 2) == 'a') {
        $arr_a[$k] = $v;
    } else if (substr($k, 2) == 'b') {
        $arr_b[$k] = $v;
    }
}

      

3) I will concatenate the two suffix arrays.

$arr_suffixes = array_merge($arr_a, $arr_b);

      

4) I slice my original array to get the part before the suffixes and the part after the suffixes.

$i = array_search(key($arr_suffixes), array_keys($arr));
$length = count($arr_suffixes);

$arr_before_suffixes = array_slice($arr, 0, $i);
$arr_after_suffixes = array_slice($arr, $i + $length);

      

5) Using array_merge, I recombine the sliced โ€‹โ€‹arrays to create the array I need.

$arr = array_merge($arr_before_suffixes, $arr_suffixes);
$arr = array_merge($arr, $arr_after_suffixes);

      

Finally, we have the correct $ arr. Isn't there a better way to do this? It seems very ugly.

+3


source to share


3 answers


You don't have a formalized rule. I'll try to guess.



$arr = array(
    '01' => 1,
    '02' => 1,
    '03a' => 1,
    '03b' => 1,
    '04a' => 1,
    '04b' => 1,
    '05a' => 1,
    '05b' => 1,
    '06' => 1,
);

uksort($arr, function($item1, $item2)
    {
    $last1 = substr($item1, -1);
    $last2 = substr($item2, -1);
    // one of the items is a number or last letters matches
    if (is_numeric($last1) || is_numeric($last2) || $last1 == $last2) 
        // simple number comparison
        return $item1 - $item2;
    else
        // natural order comparison
        return $last1 > $last2 ? 1 : -1;
    });

var_dump($arr);

      

+4


source


$arr = array(
    '01' => 1,
    '02' => 2,
    '03a' => 3,
    '03b' => 6,
    '04a' => 4,
    '04b' => 7,
    '05a' => 5,
    '05b' => 8,
    '06' => 9,
);

uksort(
    $arr,
    function($a, $b) {
        sscanf($a, '%d%s', $an, $as);
        sscanf($b, '%d%s', $bn, $bs);
        if ($as === null || $bs === null || $as === $bs) {
            return $an - $bn;
        }
        return strcmp($as, $bs);
    }
);
var_dump($arr);

      



+3


source


natsort () will help you:

$arr = array(
    '01' => 1,
    '02' => 1,
    '03a' => 1,
    '03b' => 1,
    '04a' => 1,
    '04b' => 1,
    '05a' => 1,
    '05b' => 1,
    '06' => 1,
);
$keys = array_keys($arr);
natsort($keys);
$result = array();
foreach ($keys as $key) {
    $result[$key] = $arr[$key];
}
print_r($result); // Your expected result

      

+2


source







All Articles