Created
January 30, 2018 14:46
-
-
Save imbrish/05e27a28dfc713a373044d7fd7807805 to your computer and use it in GitHub Desktop.
Locale-aware & multi-key sorting of arrays
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
if (! function_exists('lsort')) { | |
/** | |
* Sort an array according to the current locale. | |
* | |
* Order will be determined by given key or keys, which may be nested using dot notation. | |
* Alternatively a callback may be given to return value or array of values to sort with. | |
* Boolean may be supplied as a last argument to specify sort order, true for descending. | |
* | |
* More on locale sorting http://demo.icu-project.org/icu-bin/locexp?_=en_US&d_=en&x=col. | |
* | |
* @param array $array | |
* @param \Closure $callback | |
* @param array|string ...$keys | |
* @param bool $descending | |
* @return bool | |
*/ | |
function lsort(array &$array, ...$args) { | |
// We will determine the sort order from the last argument if it is a boolean. Then we will | |
// define a callback closure for extracting values for comparison from items of the array. | |
if (count($args) > 0 && is_bool(end($args))) { | |
$descending = array_pop($args); | |
} | |
else { | |
$descending = false; | |
} | |
if (count($args) == 0) { | |
$callback = null; | |
} | |
else if (count($args) == 1 && is_closure(reset($args))) { | |
$callback = array_shift($args); } | |
else { | |
$callback = function ($item) use ($args) { | |
$result = []; | |
foreach (array_flatten($args) as $key) { | |
$result[] = data_get($item, $key); | |
} | |
return $result; | |
}; | |
} | |
// To reduce number of callback calls we will first prepare array of comparator values for each | |
// element in the sorted array. We will then sort these values and in the end restore original. | |
$values = array_map(function ($item) use ($callback) { | |
if (! $callback || ! is_array($item = $callback($item))) { | |
return [$item]; | |
} | |
return $item; | |
}, $array); | |
// We will create a collator in current locale and enable sorting numbers in the natural order. | |
// Then we will sort the array by comparing consecutive values returned from callback for every | |
// item until we find a difference or run out of values. We'll optionally reverse the order. | |
$collator = new Collator(config('app.locale')); | |
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON); | |
$result = uasort($values, function ($A, $B) use ($collator, $descending) { | |
while (! empty($A) && ! empty($B)) { | |
$a = array_shift($A); | |
$b = array_shift($B); | |
if (is_numeric($a) && is_numeric($b) || $a instanceof DateTime && $b instanceof DateTime) { | |
$result = $a < $b ? -1 : ($a > $b ? 1 : 0); | |
} | |
else { | |
$result = $collator->compare($a, $b); | |
} | |
if ($result) { | |
return $descending ? - $result : $result; | |
} | |
} | |
return 0; | |
}); | |
$array = array_replace($values, $array); | |
return $result; | |
} | |
} | |
if (! function_exists('lrsort')) { | |
/** | |
* Sort an array in descending order according to the current locale. | |
* | |
* @param array $array | |
* @param \Closure $callback | |
* @param array|string ...$keys | |
* @return bool | |
*/ | |
function lrsort(&$array, ...$args) { | |
array_push($args, true); | |
return lsort($array, ...$args); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Providers; | |
use Illuminate\Support\Collection; | |
use Illuminate\Support\ServiceProvider; | |
class MacroServiceProvider extends ServiceProvider | |
{ | |
/** | |
* Register the service provider. | |
* | |
* @return void | |
*/ | |
public function register() | |
{ | |
$this->registerLocaleSortOnCollection(); | |
} | |
/** | |
* Register localeSort and localeSortDesc macros on Collection class. | |
* | |
* @return void | |
*/ | |
protected function registerLocaleSortOnCollection() | |
{ | |
Collection::macro('localeSort', function (...$args) { | |
$items = $this->items; | |
lsort($items, ...$args); | |
return new static($items); | |
}); | |
Collection::macro('localeSortDesc', function (...$args) { | |
$items = $this->items; | |
lrsort($items, ...$args); | |
return new static($items); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment