Skip to content

Instantly share code, notes, and snippets.

@leifwickland
Created May 11, 2012 17:33
Show Gist options
  • Save leifwickland/2661205 to your computer and use it in GitHub Desktop.
Save leifwickland/2661205 to your computer and use it in GitHub Desktop.
sortBy() function for PHP (inspired by Scala)
<?php
/**
* Sorts an array based on keys provided by a closure which extracts a value from the array's elements.
*
* Keys are not preserved. The original is not modified.
*
* Inspired by Scala's Seq.sortBy()
* @see http://www.scala-lang.org/api/current/index.html#scala.collection.Seq
*
* @param $array array-like To sort. (Really it just needs to be something iterable.)
* @param $descendingOrder boolean Whether the keys should be sorted in ascending or descending order.
* @param $keyExtractor Closure Function that will be passed each element of $array to produce a key for sorting. Must return an int or string.
*
* @returns a new array sorted as you requested.
*/
public static function sortBy($array, $descendingOrder, \Closure $keyExtractor)
{
$newArray = array();
foreach ($array as $value) {
$newArray[] = $value;
}
$descendingMultiplier = $descendingOrder ? -1 : 1;
$usortSucceeded = usort($newArray, function($a, $b) use ($descendingMultiplier, $keyExtractor) {
$aKey = $keyExtractor($a);
$bKey = $keyExtractor($b);
if (is_int($aKey) && is_int($bKey)) {
return $descendingMultiplier * (($aKey < $bKey) ? (-1) : (($aKey === $bKey) ? (0) : (1)));
}
if (is_string($aKey) && is_string($bKey)) {
return $descendingMultiplier * strcmp($aKey, $bKey);
}
throw new \Exception("keyExtractor must return an int or string.");
});
if (!$usortSucceeded) {
throw new \Exception("Unable to sort array.");
}
return $newArray;
}
function testSortBy() {
$a = array(
2 => array('a' => 2),
1 => array('a' => 1),
0 => array('a' => 3),
);
$sorted = Framework::sortBy($a, false, function($e) { return $e['a']; });
$this->assertTrue(is_array($sorted));
$this->assertIdentical(count($sorted), count($a));
$this->assertIdentical(reset($sorted), $a[1]);
$this->assertIdentical(next($sorted), $a[2]);
$this->assertIdentical(next($sorted), $a[0]);
}
function testSortByReverse() {
$a = array(
2 => array('a' => 2),
1 => array('a' => 1),
0 => array('a' => 3),
);
$sorted = Framework::sortBy($a, true, function($e) { return $e['a']; });
$this->assertTrue(is_array($sorted));
$this->assertIdentical(count($sorted), count($a));
$this->assertIdentical(reset($sorted), $a[0]);
$this->assertIdentical(next($sorted), $a[2]);
$this->assertIdentical(next($sorted), $a[1]);
}
function testSortByDuplicateKeys() {
$a = array(
3 => array('a' => 2),
2 => array('a' => 2),
1 => array('a' => 1),
0 => array('a' => 3),
);
$sorted = Framework::sortBy($a, false, function($e) { return $e['a']; });
$this->assertTrue(is_array($sorted));
$this->assertIdentical(count($sorted), count($a));
$this->assertIdentical(reset($sorted), $a[1]);
$this->assertIdentical(next($sorted), $a[3]);
$this->assertIdentical(next($sorted), $a[2]);
$this->assertIdentical(next($sorted), $a[0]);
}
function testSortByWithStringKeys() {
$a = array(
3 => array('a' => 'B'),
2 => array('a' => 'B'),
1 => array('a' => 'A'),
0 => array('a' => 'C'),
);
$sorted = Framework::sortBy($a, false, function($e) { return $e['a']; });
$this->assertTrue(is_array($sorted));
$this->assertIdentical(count($sorted), count($a));
$this->assertIdentical(reset($sorted), $a[1]);
$this->assertIdentical(next($sorted), $a[3]);
$this->assertIdentical(next($sorted), $a[2]);
$this->assertIdentical(next($sorted), $a[0]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment