开发者

PHP - sort hash array by key length

开发者 https://www.devze.com 2023-01-20 17:27 出处:网络
I\'ve found a few answers to sorting by value, but not key. What I\'d like to do is a reverse sort, so with:

I've found a few answers to sorting by value, but not key.

What I'd like to do is a reverse sort, so with:

    $nametocode['reallylongname']='12';
    $nametocode['shortname']='10';
    $nametocode['mediumname']='11';

I'd like t开发者_如何学JAVAhem to be in this order

  1. reallylongname
  2. mediumname
  3. shortname

mediumname shortname

Many thanks


Another solution using array_multisort:

$keys = array_map('strlen', array_keys($arr));
array_multisort($keys, SORT_DESC, $arr);

Here $keys is an array of the lengths of the keys of $arr. That array is sorted in descending order and then used to sort the values of $arr using array_multisort.


Based on @thetaiko answer, with a simpler callback :

function sortByLengthReverse($a, $b){
    return strlen($b) - strlen($a);
}

uksort($nametocode, "sortByLengthReverse");

Resources :

  • php.net - uksort()
  • php.net - Sorting arrays
  • php.net - strlen()


You can use a user defined key sort function as a callback for uksort:

function cmp($a, $b)
{
    if (strlen($a) == strlen($b))
        return 0;
    if (strlen($a) > strlen($b))
        return 1;
    return -1;
}

uksort($nametocode, "cmp");

foreach ($nametocode as $key => $value) {
    echo "$key: $value\n";
}

Quick note - to reverse the sort simply switch "1" and "-1".


Behold my powerful inline methodologies. Preserve global space for the generations to come!

uksort($data, create_function('$a,$b', 'return strlen($a) < strlen($b);'));


I have benchmarked some of sorting algorithms since performance is important for my project - here's what I've found (averaged result ran 1000x, sorted field had cca 300 elements with key size 3-50 chars):

  • 2.01 sec ... uksort with anonymous create_function (by cojam)
  • 0.28 sec ... array_multisort (by Gumbo)
  • 2.69 sec ... uksort with non-anonymous function (by Colin Herbert) - surprise for me,
  • 0.15 sec ... simple foreach + arsort

Sometime simple foreach still wins. Using dynamic PHP features has some performance penalty, obviously.


In PHP7+ you can use uksort() with spaceship operator and anonymous function like this:

uksort($array, function($a, $b) {
    return strlen($b) <=> strlen($a);
});


One limitation while sorting the keys on the basis of length is that: equal length keys are not re-ordered. Say we need to order the keys by length in descending order.

$arr = array(
    "foo 0" => "apple",
    "foo 1" => "ball",
    "foo 2 foo 0 foo 0" => "cat",
    "foo 2 foo 0 foo 1 foo 0" => "dog",
    "foo 2 foo 0 foo 1 foo 1" => "elephant",
    "foo 2 foo 1 foo 0" => "fish",
    "foo 2 foo 1 foo 1" => "giraffe"
);

debug($arr, "before sort");
$arrBad = $arr;
sortKeysDescBAD($arrBad);
debug($arrBad, "after BAD sort");
sortKeysDescGOOD($arr);
debug($arr, "after GOOD sort 2");

function sortKeysDescBAD(&$arrNew) {
    $arrKeysLength = array_map('strlen', array_keys($arrNew));
    array_multisort($arrKeysLength, SORT_DESC, $arrNew);
    //return max($arrKeysLength);
}

function sortKeysDescGOOD(&$arrNew) {
    uksort($arrNew, function($a, $b) {
        $lenA = strlen($a); $lenB = strlen($b);
        if($lenA == $lenB) {
            // If equal length, sort again by descending
            $arrOrig = array($a, $b);
            $arrSort = $arrOrig;
            rsort($arrSort);
            if($arrOrig[0] !== $arrSort[0]) return 1;
        } else {
            // If not equal length, simple
            return $lenB - $lenA;
        }
    });
}

function debug($arr, $title = "") {
    if($title !== "") echo "<br/><strong>{$title}</strong><br/>";
    echo "<pre>"; print_r($arr); echo "</pre><hr/>";
}

Output will be:

before sort
Array
(
    [foo 0] => apple
    [foo 1] => ball
    [foo 2 foo 0 foo 0] => cat
    [foo 2 foo 0 foo 1 foo 0] => dog
    [foo 2 foo 0 foo 1 foo 1] => elephant
    [foo 2 foo 1 foo 0] => fish
    [foo 2 foo 1 foo 1] => giraffe
)

after BAD sort
Array
(
    [foo 2 foo 0 foo 1 foo 0] => dog
    [foo 2 foo 0 foo 1 foo 1] => elephant
    [foo 2 foo 0 foo 0] => cat
    [foo 2 foo 1 foo 0] => fish
    [foo 2 foo 1 foo 1] => giraffe
    [foo 0] => apple
    [foo 1] => ball
)

after GOOD sort
Array
(
    [foo 2 foo 0 foo 1 foo 1] => elephant
    [foo 2 foo 0 foo 1 foo 0] => dog
    [foo 2 foo 1 foo 1] => giraffe
    [foo 2 foo 1 foo 0] => fish
    [foo 2 foo 0 foo 0] => cat
    [foo 1] => ball
    [foo 0] => apple
)

Notice the order of elephant and dog for example (or others) in two sorting methods. The second method looks better. There may be easier ways to solve this but hope this helps someone...


Take a look on uksort.


To absolutely use uksort you can do like this:

$nametocode['reallylongname']='12';
$nametocode['name']='17';
$nametocode['shortname']='10';
$nametocode['mediumname']='11';

uksort($nametocode, function($a, $b) {
   return strlen($a) - strlen($b);
});

array_reverse($nametocode, true);

Output:

Array
(
   [reallylongname] => 12
   [mediumname] => 11
   [shortname] => 10
   [name] => 17
)

I have tested this code.


The code below sorts the PHP hash array by string length of the key, then by the case-sensitive key itself, both in ascending order, using a lambda function:

uksort($yourArray, function ($a, $b) { 
   return (strlen($a) == strlen($b) ? strcmp($a, $b) : strlen($a) - strlen($b));
});

This code does the same in reverse order:

uksort($yourArray, function ($b, $a) { 
   return (strlen($a) == strlen($b) ? strcmp($a, $b) : strlen($a) - strlen($b));
});


A simple problem requires a simple solution ;-)

arsort($nametocode, SORT_NUMERIC);
$result = array_keys($nametocode);
0

精彩评论

暂无评论...
验证码 换一张
取 消