Skip to content

Instantly share code, notes, and snippets.

@amnuts
Last active June 8, 2020 20:35
Show Gist options
  • Save amnuts/8633684 to your computer and use it in GitHub Desktop.
Save amnuts/8633684 to your computer and use it in GitHub Desktop.
Sort array of objects by one or more properties of the object.
<?php
/**
* Sort an array of objects.
*
* Requires PHP 5.3+ to be installed.
*
* Will use the Intl extension to normalize characters if it's
* available.
*
* You can pass in one or more properties on which to sort. If a
* string is supplied as the sole property, or if you specify a
* property without a sort order then the sorting will be ascending.
*
* If the key of an array is an array, then it will sorted down to that
* level of node.
*
* Example usages:
*
* osort($items, 'size');
* osort($items, ['size', ['time' => SORT_DESC, 'user' => SORT_ASC]]);
* osort($items, ['size', ['user', 'forname']])
*
* @param array $array
* @param string|array $properties
*/
function osort(&$array, $properties)
{
if (is_string($properties)) {
$properties = array($properties => SORT_ASC);
}
uasort($array, function($a, $b) use ($properties) {
foreach($properties as $k => $v) {
if (is_int($k)) {
$k = $v;
$v = SORT_ASC;
}
$collapse = function($node, $props) {
if (is_array($props)) {
foreach ($props as $prop) {
$node = (!isset($node->$prop)) ? null : $node->$prop;
}
return $node;
} else {
return (!isset($node->$props)) ? null : $node->$props;
}
};
if (class_exists('\Normalizer')) {
$aProp = \Normalizer::normalize($collapse($a, $k), \Normalizer::FORM_D);
$bProp = \Normalizer::normalize($collapse($b, $k), \Normalizer::FORM_D);
} else {
$aProp = $collapse($a, $k);
$bProp = $collapse($b, $k);
}
if ($aProp != $bProp) {
return ($v == SORT_ASC)
? strnatcasecmp($aProp, $bProp)
: strnatcasecmp($bProp, $aProp);
}
}
return 0;
});
}
@SnakeME
Copy link

SnakeME commented Apr 20, 2015

Hi,

I'm using your function but ran into problems when trying to sort by Ä,Ö,Ü for example.

If I have the following random list of items:

Deutschland
Italien
Ägypten
Finnland

and sort it via your function ASC. Then I receive the following output:

Deutschland
Finnland
Italien
Ägypten

while the order should be:

Ägypten
Deutschland
Finnland
Italien

@amnuts
Copy link
Author

amnuts commented Dec 15, 2015

Sorry, I had no idea you had posted this comment! So apologies for the delay in responding.

If you're running PHP 5.3 and above (which, really, you should be) then you can try using the Normalizer class (part of the Intl extension). I've updated the gist to include that class.

Using it is the same. Eg:

$a = [
    (object)['place' => 'Deutschland', 'price' => 30],
    (object)['place' => 'Italien', 'price' =>  18],
    (object)['place' => 'Ägypten', 'price' =>  70],
    (object)['place' => 'Finnland', 'price' => 120]
];
osort($a, 'place');
var_dump($a);

would result in:

array(4) {
  [2] =>
  class stdClass#3 (2) {
    public $place =>
    string(8) "Ägypten"
    public $price =>
    int(70)
  }
  [0] =>
  class stdClass#1 (2) {
    public $place =>
    string(11) "Deutschland"
    public $price =>
    int(30)
  }
  [3] =>
  class stdClass#4 (2) {
    public $place =>
    string(8) "Finnland"
    public $price =>
    int(120)
  }
  [1] =>
  class stdClass#2 (2) {
    public $place =>
    string(7) "Italien"
    public $price =>
    int(18)
  }
}

@rodmar35
Copy link

Hi
Thanks for your code.
Just, in Example usages: osort($items, array('size', array('time' => SORT_DESC, 'user' => SORT_ASC));
You miss a ) at the end ;-)
osort($items, array('size', array('time' => SORT_DESC, 'user' => SORT_ASC)));

@amnuts
Copy link
Author

amnuts commented Sep 16, 2017

@rodmar35 👍 Thanks for the feedback - corrected now!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment