Reputation: 335
I've been playing around and seeing if I can set up a more dynamic method in PHP.
usort(
$dataset,
function($a, $b){
return strcasecmp($a[$this->parameters], $b[$this->parameters]);
}
);
This line would sort array elements in alphabetically descending order. However, if we were to swap variables $a
and $b
whether in function($a, $b)
or ($a[$this->parameters], $b[$this->parameters])
we will get the opposite effect (descending order).
As I've looked into the matter, there is such a thing as "variable variables" in PHP. An example of this coulde be:
$a="hello";
$$a="oops";
echo($hello);die;
However, if I similarly try to implement this within the code of line above I get an error.
$first = 'b';
$second = 'a';
usort($dataset, function($$first, $$second){ return strcasecmp($a[$this->parameters], $b[$this->parameters]); });
Error:Parse error: syntax error, unexpected '$', expecting variable (T_VARIABLE)
.
The idea is to be able to reverse the effect base on an iff statement result to redefine $first
and $second
variables. Otherwise one would need to duplicate almost identical code.
Am I missing something? Maybe some other way to achieve this?
Upvotes: 1
Views: 368
Reputation: 48031
Let's back up the bus on this XY Problem. In virtually every use case, implementing variable variables is at worst an ill-considered antipattern and at best a symptom that data which should be array-typed is coded as a non-array data type.
Variable variables often confuse even high quality IDEs (the software that devs write their code in), so using these valid coding techniques can often trigger annoying false-positive warnings in otherwise extremely helpful code editors.
As for how you can avoid variable variables while calling usort()
-- simply supply a conditionally positive or negative factor to affect the result of the 3-way comparison.
Code: (Demo)
$direction = 'asc';
$factor = $direction === 'desc' ? -1 : 1;
$c = 'columnName';
usort(
$array,
fn($a, $b) => $factor * ($a[$c] <=> $b[$c])
);
var_export($array);
Depending on specific circumstances, it may be less cryptic to, instead, call array_multisort()
. (Demo)
$direction = 'desc';
$c = 'columnName';
array_multisort(
array_column($array, $c),
$direction === 'desc' ? SORT_DESC : SORT_ASC,
$array
);
var_export($array);
Finally, if you are looking for a robust, general-use dynamic sorting function, you might be inspired by my answer to Sort array of associative arrays on multiple columns using specified sorting rules.
Upvotes: 0
Reputation: 4400
I would like to expand a bit on @ArSeN's great answer to add a little more computer science context to why this simply isn't "a thing." If you take a look at how functions are implemented at the processor level (ASM Language) you quickly realize that the argument names passed to a function are not even used in the binary program. They are really only there to make your life as a programmer humaine. Most calling conventions implement function arguments on the stack frame as space between the functions. They are accessed at their respective memory offsets inside of the function. Most C books and courses will cover this concept in detail, but in simple english this means that the name of the argument doesn't matter as only the order of the argument is used by the eventual binary. This has been abstracted far, far away in PHP, however, it is still quite relevant and something you should understand if you intend to programming at a professional level.
Upvotes: 1
Reputation: 5258
Resolving dynamic names of variables is something that happens at runtime, rather than interpreter (aka compile) time. Therefore, you can not pass it as dynamic paramaters which you expected to be able to do.
Given that, this is not possible for one simple reason: Even if you would manage to pass different names at compile time, it would only be true for one set of values to compare. What would keep the callback-call from passing a<b
, then a>b
, then a==b
(imagine them as values)? Nothing, and exactly that would happen.
That being said, you can try and validate which value is smaller before passing it to the final callback, but this only adds an extra layer rather than always sorting the same way (or even sorting at all):
usort($dataset, function($a, $b)
{
if ($a > $b) {
return $b <=> $a;
}
return $a <=> $b;
});
var_dump($dataset);
// output
array(3) {
[0]=>
int(3)
[1]=>
int(1)
[2]=>
int(7)
}
I am fully aware that this does not solve your problem at all. I am just trying to demonstrate that it wont even work that way.
I think the key fact here is that you define the sort mechanism in your callback, and hence you have to make sure that you sort it ascending or descending in that definition, since that is what it exists for!
And on a side note I think sorting callbacks became really easy to create in PHP since the spaceship operator:
// defines: sort ASC
usort($dataset, function($a, $b) { return $a <=> $b; });
// defines: sort DESC
usort($dataset, function($a, $b) { return $b <=> $a; });
And even more so with the arrow functions since PHP 7.4:
// ASC
usort($dataset, fn($a, $b) => $a <=> $b);
// DESC
usort($dataset, fn($a, $b) => $b <=> $a);
In conclusion, I fully understand where you are coming from, but maybe you are trying to solve a problem that is not even really there?
Upvotes: 2