ok using usort with a function is not so complicated
This is what i had before in my linear code
function merchantSort($a,$b){
return ....// stuff;
}
$array = array('..','..','..');
to sort i simply do
usort($array,"merchantSort");
Now we are upgrading the code and removing all global functions and putting them in their appropriate place. Now all the code is in a class and i can't figure out how to use the usort function to sort the array with the parameter that is an object method instead of a simple function
class ClassName {
...
private function merchantSort($a,$b) {
return ...// the sort
}
public function doSomeWork() {
...
$array = $this->someThingThatReturnAnArray();
usort($array,'$this->merchantSort'); // ??? this is the part i can't figure out
...
}
}
The question is how do i call an object method inside the usort() function
Make your sort function static:
private static function merchantSort($a,$b) {
return ...// the sort
}
And use an array for the second parameter:
$array = $this->someThingThatReturnAnArray();
usort($array, array('ClassName','merchantSort'));
open the manual page http://www.php.net/usort
see that the type for $value_compare_func is callable
click on the linked keyword to reach http://php.net/manual/en/language.types.callable.php
see that the syntax is array($this, 'merchantSort')
You need to pass $this e.g.: usort( $myArray, array( $this, 'mySort' ) );
Full example:
class SimpleClass
{
function getArray( $a ) {
usort( $a, array( $this, 'nameSort' ) ); // pass $this for scope
return $a;
}
private function nameSort( $a, $b )
{
return strcmp( $a, $b );
}
}
$a = ['c','a','b'];
$sc = new SimpleClass();
print_r( $sc->getArray( $a ) );
In this example I am sorting by a field inside the array called AverageVote.
You could include the method inside the call, which means you no longer have the class scope problem, like this...
usort($firstArray, function ($a, $b) {
if ($a['AverageVote'] == $b['AverageVote']) {
return 0;
}
return ($a['AverageVote'] < $b['AverageVote']) ? -1 : 1;
});
In Laravel (5.6) model class, I called it like this, both methods are public static, using php 7.2 on windows 64 bit.
public static function usortCalledFrom()
public static function myFunction()
I did call in usortCalledFrom() like this
usort($array,"static::myFunction")
None of these were work
usort($array,"MyClass::myFunction")
usort($array, array("MyClass","myFunction")
simplest way would be to make an arrow function which calls your sort function like so:
uasort($array, fn($a, $b) => $this->mySortFunction($a, $b, $optionalAdditionalParam))
...
private function mySortFunction($a, $b) {
return -1; // replace with sort logic
}
This worked for me, I hope it helps someone:
usort($array, [ClassName::class, "functionName"]);
Replace "ClassName" with the name of your PHP Class and "functionName" with the name of your private static function.
If everything occurs inside the own class this one works as well:
usort($array, [self::class, "functionName"]);
Replace "functionName" with the name of your private static function.
Related
I have a "static class" used as a singleton (because there's never a need to have multiple instances), and other classes that extend it and are used in the same way.
At some point in the processing, the extended class will call parent::doThis(), with doThis ultimately doing a usort.
The callback of the usort should be in the calling class, as each will handle sorting differently. Can something like "class::method()" be the string for a usort callback, and if so, is there a way for the parent class to know which class called it without me passing that as an argument, so that it can name the calling class's callback for the usort?
class parentClass {
protected static function doThis($data) {
// do stuff, then
usort($data, "myCallingClass::cmp()"
}
}
based on some means of the parent determining what myCallingClass is, or does it need to be
class parentClass {
protected static function doThis($data, $calling_class) {
// do stuff, then
usort($data, $calling_class . "::cmp()"
}
}
I think you should be able to do this with Late Static Bindings:
usort($data, function($a, $b) {return(static::cmp($a, $b));});
you can add static function name() { return "myCallingClass"; } to each class with it's own name. Then all you need is to call
usort($data, static::name() . "::cmp()");
static modifer assures you that method will be called from inheriting class if it has one.
A simple solution is to use a well-known callback name in combination with get_called_class:
class Numbers
{
static function sort() {
$things = [1,2,3,4,5];
usort($things, [get_called_class(), 'cmp']);
print join(' ', $things);
}
}
class Mod2 extends Numbers {
static function cmp($a, $b) {
return $a % 2 - $b % 2;
}
}
Mod2::sort(); // 4 2 5 3 1
A not-so-simple, but correct solution is to forget all this "static classes" and "singletons" stuff and use objects the way they are intended to be used:
class Numbers
{
function sort() {
$things = [1,2,3,4,5];
usort($things, [$this, 'cmp']);
print join(' ', $things);
}
}
class Mod2 extends Numbers {
function cmp($a, $b) {
return $a % 2 - $b % 2;
}
}
(new Mod2)->sort(); // 4 2 5 3 1
I am trying to use the usort() function in PHP. I am not sure how to call the comparison function. Here is my code below. I tried $this->comparator and that didnt help. This would have been easy if comparator was a function that didn't need to access the class's member variables.
class A {
$p1 // non-associative array
$p2 // non-associative array
public function comparator($a, $b)
{
// the usual comparison stuff
if ($this->p1[$a] == $this->p2[$b])
return 0;
else ($this->p1[$a] < $this->p2[$b])
return 1;
else
return -1;
}
public function sorting()
{
// after some code
$some_array = array(..);
usort($some_array, "comparator") // <--- ERROR here: does not recognize comparator
}
}
You need to specify the sort function in a callable way:
usort($some_array, array($this, "comparator"));
Even though the type callable didn't exist prior to PHP 5.4, referencing methods works the same way.
it is possible to assign to a class variable a function at runtime to be executed? a kind of "function pointer" like C
something like this: (this won't work because sum is out of the scope of A, but this is the pattern i mean)
class A {
public $function_name;
public functon run($arg1,$arg2){
$function_name($arg1,$arg2);
}
}
function sum($a,$b){
echo $a+$b;
}
$a=new A();
$a->function_name='sum';
$a->run();
[edit]
i know there is "call_user_func" but it need as i understand to have the function in the scope or use a public class method
You could use an anonymous function if you use PHP >5.3.0:
$sum = function($a, $b) {
return $a+$b;
}
$a->function_name = $sum;
Using call_user_func_array:
<?php
class A {
public $function_name;
public function run($arg1,$arg2){
return call_user_func_array( $this->function_name, array($arg1, $arg2 ) );
}
}
function sum($a,$b){
return $a+$b;
}
$a=new A();
$a->function_name= 'sum';
var_dump( $a->run(1,1) ); //2
?>
It works regardless of scope. You just gotta call it using call_user_func. I also fixed a couple of typos in your example.
<?php
class A {
public $function_name;
public function run($arg1, $arg2) {
call_user_func($this->function_name, $arg1, $arg2);
}
}
function sum($a, $b){
echo $a + $b;
}
$a = new A();
$a->function_name = 'sum';
$a->run(2, 3);
?>
Live example
Another way is to make use variable variables (applicable to object method)
public static function sum($arg1, $arg2)
{
..
}
public function run($arg1, $arg2)
{
$func = $this->function_name;
$func( $arg1, $arg2); <-- procedural call
self::$func($arg1, $arg2); <-- static method call
}
Use any variation of the Callback pseudo type.
Use it with call_user_func or call_user_func_array
The manual gives great examples of usage for the above.
Also see the new php 5.4 Closure::bindTO method if you want to be able to easily use the $this keyword in it.
I would like to have this $sort_flags array available within the compare_by_flags function, but I didn't find a way to this, is it possible?
public function sort_by_rank(array $sort_flags = array()) {
uasort($this->search_result_array, array($this, 'compare_by_flags'));
}
private static function compare_by_flags($a, $b) {
// I want to have this $sort_flags array here to compare according to those flags
}
If you use php < 5.3 then you can just use instance variables:
public function sort_by_rank(array $sort_flags = array()) {
$this->sort_flags = $sort_flags;
uasort($this->search_result_array, array($this, 'compare_by_flags'));
}
private static function compare_by_flags($a, $b) {
// I want to have this $sort_flags array here to compare according to those flags
}
otherwise - use closures:
public function sort_by_rank(array $sort_flags = array()) {
uasort($this->search_result_array, function($a, $b) use ($sort_flags) {
// your comparison
});
}
You don't mention what you want to achieve by passing the $sort_flags variable, but you might find this answer of mine useful (either as it stands, or as an example if you want to achieve something different).
You could set it as class static property, like this:
public function sort_by_rank(array $sort_flags = array()) {
self::$_sort_flags = $sort_flags;
uasort($this->search_result_array, array($this, 'compare_by_flags'));
}
private static function compare_by_flags($a, $b) {
// Read self::$_sort_flags
// I want to have this $sort_flags array here to compare according to those flags
}
Also you could try this, as of PHP 5.3
uasort($array, function($a, $b) {
self::compare_by_flags($a, $b, $sort_flags);
});
Referencing a method parameter variable from within a sub-function of that method doesn't seem to work even when global is specified.
public function sortArray(&$array, $keyToCompare){// BOOL sortArray( ARR &$array, STR $keyToCompare )
function cmpVals($pairA, $pairB){
global $keyToCompare;
return strcmp($pairA[$keyToCompare], $pairB[$keyToCompare]);
}
return uasort($array, 'cmpVals');
}
Is it even possible to reference a method parameter from within a sub-function? If so... how?
For my specific example above I realise I could use a closure to achieve the same goal but I don't want the script to be PHP 5.3 dependent.
Any reason you can't make it static?
class YourClassName {
public static $keyToCompare;
public function sortArray(&$array, $keyToCompare){// BOOL sortArray( ARR &$array, STR $keyToCompare )
self::$keyToCompare = $keyToCompare;
function cmpVals($pairA, $pairB){
$keyToCompare = YourClassName::$keyToCompare;
return strcmp($pairA[$keyToCompare], $pairB[$keyToCompare]);
}
return uasort($array, 'cmpVals');
}
}
You seemed to be already using OOP in PHP 5.3. Might as well create a sorter class?
class Sorter{
private $key;
function __construct($key){
$this->key = $key;
}
private function compare($a, $b){
return strcmp($a[$this->key], $b[$this->key]);
}
public function sort($a){
uasort($a, array($this, 'compare'));
}
}
Another option is an anonymous function:
public function sortArray(&$array, $keyToCompare){
return uasort($array, function($pairA, $pairB) uses ($keyToCompare) {
return strcmp($pairA[$keyToCompare], $pairB[$keyToCompare]);
});
}
Untested, on a train :D, but see the documentation for more info.
FYI, the pass by reference is unnecessary since you don't modify the array.. PHP won't make a copy so there is no memory issues passing by value.