0

I have this function in my old php5 code that will accept a variable number of parameters and perform sorting based on the parameters:

function array_alternate_multisort(){
    $arguments = func_get_args();
    $arrays     = $arguments[0];
    for ($c = (count($arguments)-1); $c > 0; $c--)
    {
        if (in_array($arguments[$c], array(SORT_ASC , SORT_DESC)))
        {
                continue;
        }
        $compare = create_function('$a,$b','return strcasecmp($a["'.$arguments[$c].'"], $b["'.$arguments[$c].'"]);');
        usort($arrays, $compare);
        if ($arguments[$c+1] == SORT_DESC)
        {
                $arrays = array_reverse($arrays);
        }
    }
    return $arrays ;
}

I call it like this:

$alliances = array_alternate_multisort($alliances, "output", SORT_DESC, "score", SORT_DESC);

How can I replace this with a function without calling create_function()?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
rubo77
  • 19,527
  • 31
  • 134
  • 226
  • An alternate logic would be to use the `usort` function. On each invocation of callback you get two items and you need to compare the `output` for greater/lesser; if they're same then compare `score` and so on. – Salman A Jun 28 '20 at 11:17
  • I must say the `in_array()` check is not very robust/reliable. If you were to sort an indexed array and wanted to pass a sorting rule for column 4, it would be considered `SORT_DESC` and completely skip that sorting rule. https://3v4l.org/SpYYR – mickmackusa Jun 28 '20 at 13:45

2 Answers2

1

You can use an anonymous function instead:

$compare = function ($a, $b) use ($arguments, $c) {
    return strcasecmp($a[$arguments[$c]], $b[$arguments[$c]]);
};

Untested but should be close enough

The use keyword allows you to inherit variables from the parent scope inside your function.

M. Eriksson
  • 13,450
  • 4
  • 29
  • 40
1

First of all, I'll argue that if you have the ability to hardcode this into your script:

$alliances = array_alternate_multisort($alliances, "output", SORT_DESC, "score", SORT_DESC);

then you can just as easily completely scrap your custom function and just write this:

Code: (Demo)

array_multisort(...[
    array_column($alliances, 'output'),
    SORT_DESC,
    array_column($alliances, 'score'),
    SORT_DESC,
    &$alliances
]);

This will do EVERYTHING that your custom function will do and more WITHOUT introducing ANY custom function.

This is a very concise, totally native, and instantly readable technique. Using this means that:

  • you will not confine your script to only SORT_ASC and SORT_DESC; there are other useful sorting flags to enjoy for specific scenarios.
  • you can opt to omit the sorting direction parameters if you wish to use SORT_ASC (the default sorting flag).
  • you modify the input array by reference like other native sorting functions.

Now anything beyond the above is going to introduce unnecessary convolution. To keep this hypothetical (which, again, I don't endorse) demonstration simple I'll insist that the sorting direction flags are required like in your original snippet.

Code: (Demo)

function array_alternate_multisort($array, ...$args) {
    foreach ($args as $i => $arg) {
        $sortParams[] = $i & 1 ? $arg : array_column($array, $arg);
    }
    $sortParams[] = &$array;
    array_multisort(...$sortParams);
    return $array;
}
  • & 1 is a bitwise odd check. If the index is odd, push the constant into $sortParams otherwise push the column data into $sortParams.

This answer belongs to a family of similar answers that use the splat operator to unpack parameters into a array_multisort() call.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136