php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #68622 Function to sort an array by a particular key
Submitted: 2014-12-18 15:49 UTC Modified: 2014-12-29 10:44 UTC
From: craig at craigfrancis dot co dot uk Assigned: levim (profile)
Status: Not a bug Package: Arrays related
PHP Version: Irrelevant OS:
Private report: No CVE-ID: None
 [2014-12-18 15:49 UTC] 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]);
		}
	}


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-12-28 18:00 UTC] levim@php.net
-Status: Open +Status: Not a bug -Assigned To: +Assigned To: levim
 [2014-12-28 18:00 UTC] 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-28 18:07 UTC] 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-29 10:44 UTC] craig at craigfrancis dot co dot uk
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);
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Apr 24 10:01:31 2024 UTC