Better PHP usort() - php

I needed to merge and then sort two arrays with dissimilar data structures (can't be sorted in the MySQL query) but where both had a created_on field.
So I am using usort() with a custom function.
In my controller
usort(merged_array, 'sort_records');
In my helper functions
if(!function_exists('sort_records')){
function sort_records($a,$b){
if ( $a['created_at'] == $b['created_at'] )
return 0;
if ( $a['created_at'] < $b['created_at'] )
return -1;
return 1;
}
}
I would like to make this sort_records() function reusable. So I can use it with other arrays. Maybe something like..
function sort_records($a,$b,$index){
if ( $a[$index] == $b[$index] )
return 0;
if ( $a[$index] < $b[$index] )
return -1;
return 1;
Is this possible with usort() since when you call the function it does not take parameters at all? Is there another option?

You can create a class
class SortRecord
{
private $index;
public function __construct($index)
{
$this->index = $index;
}
public function sort_records($a, $b)
{
if ( $a[$this->index] == $b[$this->index] )
return 0;
if ( $a[$this->index] < $b[$this->index] )
return -1;
return 1;
}
}
then you can pass it to usort.
$obj = new SortRecord('created_at');
usort($merged_array, array($obj, 'sort_records'));

Put usort inside sort_records and use anonymous function, like so:
function sort_records(&$array,$index){
return usort($array, function ($a, $b) use ($index) {
if ( $a[$index] == $b[$index] )
return 0;
if ( $a[$index] < $b[$index] )
return -1;
return 1;
});
}
Then you can call it with whatever index you need
sort_records($array, 'created_at');

You could also use the use keyword on your usort, but you'd have to declare the inner function as anonymous :
function better_usort($array, $index) {
return usort($array, function($a, $b) use($index){
if ($a[$index] == $b[$index])
return 0;
if ($a[$index] < $b[$index])
return -1;
return 1;
});
}
And then you can call it with
better_usort($merged_array, 'created_at');

Related

About php7 Uniform Variable Syntax, nested functions

I'm try to solve a task which uses new functions php7 uniform variable syntax nested () support foo()() (https://wiki.php.net/rfc/uniform_variable_syntax).
I need write function test for this code:
$sum = function($a, $b) { return $a + $b; };
test(6)(2)(3)($sum); // 11
test(3)(1)($sum); // 4
test(3)(3)('pow'); // 27
I don't found any explanation for this feature. Where can I find how to use it? I see that I must return function name in function test, but how to pass argument?
Thanks all for help. It's something like this:
<?php
function test($a) {
echo '<br/>';
$arr[] = $a;
return $mf = function($b) use(&$mf, &$a, &$arr) {
if(gettype($b) == 'object') {
echo(array_reduce($arr, $b));
} elseif (gettype($b) == 'string') {
if($b == 'pow') {
echo array_reduce($arr, function ($carry, $a) {
return !empty($carry) ? pow($carry, $a) : $a;
});
}
} elseif (gettype($b) == 'integer') {
$arr[] = $b;
}
return $mf;
};
}
$sum = function($a, $b) { return $a + $b; };
test(6)(2)(3)($sum); // 11
test(3)(1)($sum); // 4
test(3)(3)('pow'); // 27
This is more about nested recursive functions, or currying, than that rfc. That rfc just enabled the syntax that supported it.
This uses recursion until you pass a callable:
function test($var) {
$values = [$var];
$function = function($callback) use (&$values, &$function) {
if (is_callable($callback)) {
return array_reduce(array_slice($values, 1), $callback, $values[0]);
}
$values[] = $callback;
return $function;
};
return $function;
}
Because your functions expect two parameters but your nesting could have unlimited parameters, it's best to use an array and array reduce.
However, since multiplication functions like pow won't work with a null initial value, you can specify the initial value as the first passed parameter from the array.

How to pass usort() a parameter?

My code:
function sortx($a, $b) {
if(!strpos($a["p_title"],'apple ipad')) {
return -1;
}
return 1;
}
usort($arr, 'sortx');`
In above function I want to pass:
$sort_text='apple ipad';
, when calling function instead of hardcoding apple ipad into strpos(). How can I accomplish that?
Call it with a closure:
$sort_text='apple ipad';
usort(
$arr,
function ($a, $b) use ($sort_text) {
if(!strpos($a["p_title"], $sort_text)) {
return -1;
}
return 1;
}
);
and you can pass additional arguments with the use clause

PHP function doesn't work with variables

Hi I have the following function:
foreach($array_1 as &$arr){
usort($arr,"cmp");
}
function cmp($a, $b) {
if ($a[2] == $b[2]) {
return 0;
}
return ($a[2] < $b[2]) ? -1 : 1;
}
If I do it like this it works, but if I put a variable instead of 2 which == 2 and a number the function doesn't work. Why is this and how can I work around it? I want to be able to change the number to sort an array based on variable input.
The code that doesn't work is:
$type = 2;
foreach($array_1 as &$arr){
usort($arr,"cmp");
}
function cmp($a, $b) {
if ($a[$type] == $b[$type]) {
return 0;
}
return ($a[$type] < $b[$type]) ? -1 : 1;
}
For PHP 5.3+, which hopefully you should be using, the solution is simple using a closure:
$index = 2;
foreach ($array_1 as &$arr) {
usort($arr, function ($a, $b) use ($index) {
return $a[$index] - $b[$index];
});
}
For PHP 5.2-, you could use global variables (YUCK!!) or a class:
class Sort {
protected $index;
public function __construct($index) {
$this->index = $index;
}
public function compare($a, $b) {
return $a[$this->index] - $b[$this->index];
}
}
$index = 2;
foreach ($array_1 as &$arr) {
usort($arr, array(new Sort($index), 'compare'));
}

pass argument into sort function

I am using a uasort function like this:
uasort($entity_list, 'sortArray');
function sortArray($a, $b) {
if($a['fixed_column_name'] == $b['fixed_column_name']) {
return 0;
}
return ($a['fixed_column_name'] < $b['fixed_column_name']) ? -1 : 1;
}
I would like to pass a parameter to the sortArray function, like this:
uasort($entity_list, 'sortArray($arg)');
function sortArray($a, $b, $arg) {
$larg = $arg;
if($a[$larg] == $b[$larg]) {
return 0;
}
return ($a[$larg] < $b[$larg]) ? -1 : 1;
}
If you're using PHP 5.3+ (and you really should at this point in time), you can use closures:
uasort($entity_list, function ($a, $b) use ($arg) {
if ($a[$arg] == $b[$arg]) {
return 0;
}
return ($a[$arg] < $b[$arg]) ? -1 : 1;
});
Otherwise, you'll have to work around doing the same thing using global variables (oh noes!) or a class.
You could always use a class:
$sorter = new Sorter($arg);
usort($entity_list, array($sorter, "sort")); //will use $sorter->sort as callback
class Sorter {
function __construct($arg) {
$this->arg = $arg;
}
function sort($a, $b) {
// sort using $a, $b and $this->arg
}
}
Closures are nicer though ^^
Take a look at Example #4 using a closure on the usort() page of the PHP manual. The same technique can be used with all the basic sorts, including uasort()

sort associative array codeigniter php

Here is what I want to do:
$newArray = array();
foreach($student as $s){
$newArray[$s->id][$s->grade] = $s;
}
I want to sort the students by their grades (more of a group than a sort) but I just want the grades to be sorted not the id. I could have don't this:
$newArray[$s->id] = $s->grade
asort($newArray)
but I need the remaining data in $s. Also, there is huge chunk of data associated with each student which I want to maintain.
How can I achieve such a sorting?
Edit:
Sine you're working in a framework, best declare your sort callback as a member function (inside the same class as where you'll be needing it, of course):
private function sortCB(array $a, array $b)
{//the array type hinting in arguments is optional
$i = array_keys($a);//but highly recommended
$j = array_keys($b);
if (end($i) === end($j))
{
return 0;
}
//replace '>' with '<' if you want to sort descending
return (end($i) > end($j) ? 1 : -1);//this is ascending
}
Then, in the method where the actual sorting is needed:
uasort($theArray,array($this,'sortCB'));
For more examples, see the docs. I've added a full class example at the end of this (bulky) answer
I've tried this on writecodeonline, which isn't all too good at this kind of stuff, but this did work:
$foo = array_fill_keys(array('foo','bar','q','Bond'),array());
$i = '256';
foreach($foo as $k=>$v)
{
$foo[$k][$i] = $k;
$i = (string)((int)$i%2 === 0 ? ((int)$i/2)+1 : (int)$i*3);
}
function sortCB($a,$b)
{
$i = array_keys($a);
$j = array_keys($b);
if (end($i) === end($j))
{
return 0;
}
return (end($i) > end($j) ? 1 : -1);
}
uasort($foo,'sortCB');
var_dump($foo);
But since you're using a framework, you might do well declaring that function as a member function private function sortCB(array $a,array $b), and use it like so:
uasort($foo,array($this, 'sortCB'));
There might be some more info on how best to use this callback function in a class context here
Full example + usage (tested and working):
class test
{
public $foo = null;
public function __construct()
{
$this->foo = array_fill_keys(array('foo','bar','q','Bond'),array());
$i = '256';
foreach($this->foo as $k=>$v)
{
$this->foo[$k][$i] = $k;
$i = (string)((int)$i%2 === 0 ? ((int)$i/2)+1 : (int)$i*3);
}
}
private function sortCB($a,$b)
{
$i = array_keys($a);
$j = array_keys($b);
if (end($i) === end($j))
{
return 0;
}
return (end($i) > end($j) ? 1 : -1);
}
public function sortFoo()
{
uasort($this->foo,array($this,'sortCB'));
print_r($this->foo);
return $this->foo;
}
}
$bar = new test();
$arr = $bar->sortFoo();
You can do something like:
foreach($student as $s){
$newArray[$s->id] = $s;
}
usort($newArray, function ($a, $b) { return $a->grade - $b->grade; });
Edit
For later versions that don't support anonymous functions you can define comparison function first:
function sortByGrade($a, $b)
{
return $a->grade - $b->grade;
}
usort($newArray, 'sortByGrade');
But if you get this data from db it would be easier to order it in your sql query. If you use ORM you can use its associated method.

Categories