Req #68622 [Nab]: Function to sort an array by a particular key
| From: | craig at craigfrancis dot co dot uk | Date: | Mon, 29 Dec 2014 10:44:47 +0000 |
| Subject: | Req #68622 [Nab]: Function to sort an array by a particular key | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-189307@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=68622&edit=1
ID: 68622
User updated by: craig at craigfrancis dot co dot uk
Reported by: craig at craigfrancis dot co dot uk
Summary: Function to sort an array by a particular key
Status: Not a bug
Type: Feature/Change Request
Package: Arrays related
PHP Version: Irrelevant
Assigned To: levim
Block user comment: N
Private report: N
New Comment:
Fair enough... I just thought that this was a pretty common use case that it might be better to have
a function that everyone could use.
Says he remembering that I should have used the SORT_NUMERIC/NATURAL/STRING/REGULAR constants, as
the function names are not particularly easy to remember (e.g. strnatcasecmp).
--------------------------------------------------
<?php
function array_key_sort(&$array, $key, $sort_flags = SORT_STRING, $sort_order = SORT_ASC) {
$array_key_sort = new array_key_sort($key);
switch ($sort_flags & ~SORT_FLAG_CASE) { // ref https://github.com/php/php-src/blob/master/ext/standard/array.c#L144
case SORT_NUMERIC:
$type = 'numeric';
break;
case SORT_NATURAL:
$type = ($sort_flags & SORT_FLAG_CASE ? 'strnatcasecmp' : 'strnatcmp');
break;
case SORT_STRING:
case SORT_REGULAR:
default:
$type = ($sort_flags & SORT_FLAG_CASE ? 'strcasecmp' : 'strcmp');
break;
}
uasort($array, array($array_key_sort, $type));
if ($sort_order == SORT_DESC) { // Cannot merge type and order
$array = array_reverse($array);
}
}
if (!defined('SORT_NATURAL')) define('SORT_NATURAL', 6); // PHP 5.4+
if (!defined('SORT_FLAG_CASE')) define('SORT_FLAG_CASE', 8);
class array_key_sort {
private $key = NULL;
public function __construct($key) {
$this->key = $key;
}
public function strcmp($a, $b) {
return strcmp($a[$this->key], $b[$this->key]);
}
public function strcasecmp($a, $b) {
return strcasecmp($a[$this->key], $b[$this->key]);
}
public function strnatcmp($a, $b) {
return strnatcmp($a[$this->key], $b[$this->key]);
}
public function strnatcasecmp($a, $b) {
return strnatcasecmp($a[$this->key], $b[$this->key]);
}
public function numeric($a, $b) {
if ($a[$this->key] == $b[$this->key]) {
return 0;
}
return ($a[$this->key] < $b[$this->key] ? -1 : 1);
}
}
?>
e.g.
array_key_sort($results, 'score', SORT_NUMERIC, SORT_DESC);
array_key_sort($products, 'sort', SORT_NUMERIC);
Previous Comments:
------------------------------------------------------------------------
[2014-12-28 18:07:52] levim@php.net
Here is a working example (PHP 5.3+) of what I posted in the last comment:
<?php
$f = function($f, $select) {
return function() use($f, $select) {
return call_user_func_array(
$f,
array_map($select, func_get_args())
);
};
};
function select_offset($offset) {
return function($data) use($offset) {
return $data[$offset];
};
}
$array = array(
array('name' => 'Bob'),
array('name' => 'Bird'),
);
uasort($array, $f('strnatcmp', select_offset('name')));
var_dump($array);
?>
------------------------------------------------------------------------
[2014-12-28 18:00:17] levim@php.net
I can generalize this pattern into two functions; here is an example:
<?php
$f = function(callable $select, callable $f) {
return function(...$args) use($select, $f) {
return $f(...array_map($f, $args));
};
};
function select_offset($offset) {
return function($data) {
return $data[$offset];
};
}
$array = [
['name' => 'Bob'],
['name' => 'Bird'],
];
uasort($array, $f('strnatcmp', select_offset('name')));
var_dump($array);
?>
However, I genuinely think that writing it out explicitly is better:
<?php
uasort($array, function($a, $b) {
return strnatcmp($a['name'], $b['name']);
});
?>
Also note that any function we add won't help you on a PHP 5.1 system since it would not be
backported.
------------------------------------------------------------------------
[2014-12-18 15:49:47] craig at craigfrancis dot co dot uk
Description:
------------
As hinted by example 3:
http://php.net/array_multisort
I find that I'm frequently sorting arrays by a particular key (admittedly not from the
database, as that has the ORDER BY clause).
This can be done with:
function sort_name($a, $b) {
return strnatcmp($a['name'], $b['name']);
}
uasort($data, 'sort_name');
Or perhaps with PHP 5.3 anonymous functions:
uasort($data, function ($a, $b) {
return strnatcmp($a['name'], $b['name']);
});
But perhaps we could introduce a function that can reduce the amount of code required (making it
easier to read):
array_key_sort($data, 'name');
array_key_sort($data, 'name', SORT_STRING);
The example implementation below uses a class, as unfortunately my code needs to run on a PHP 5.1
server (so no anonymous functions).
Test script:
---------------
function array_key_sort(&$array, $key, $type = 'numerical') {
$array_key_sort = new array_key_sort($key);
uasort($array, array($array_key_sort, $type));
}
class array_key_sort {
private $key = NULL;
public function __construct($key) {
$this->key = $key;
}
public function numerical($a, $b) {
if ($a[$this->key] == $b[$this->key]) {
return 0;
}
return ($a[$this->key] < $b[$this->key] ? -1 : 1);
}
public function strcmp($a, $b) {
return strcmp($a[$this->key], $b[$this->key]);
}
public function strcasecmp($a, $b) {
return strcasecmp($a[$this->key], $b[$this->key]);
}
public function strnatcmp($a, $b) {
return strnatcmp($a[$this->key], $b[$this->key]);
}
public function strnatcasecmp($a, $b) {
return strnatcasecmp($a[$this->key], $b[$this->key]);
}
}
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=68622&edit=1