Skip to content

Instantly share code, notes, and snippets.

@amnuts

amnuts/osort.php

Last active Jun 8, 2020
Embed
What would you like to do?
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

This comment has been minimized.

Copy link

@SnakeME 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

This comment has been minimized.

Copy link
Owner Author

@amnuts 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

This comment has been minimized.

Copy link

@rodmar35 rodmar35 commented Sep 14, 2017

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

This comment has been minimized.

Copy link
Owner Author

@amnuts 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
You can’t perform that action at this time.