Skip to content

Instantly share code, notes, and snippets.

@cusster

cusster/msort.php

Last active Nov 12, 2018
Embed
What would you like to do?
<?php
/**
* Get value at a given path expressed in dot notation.
*
* @param array $arr
* @param string $path
* @return mixed
*/
function array_dot_notation(array $arr, string $path)
{
$path = array_map(function($i) {
return trim($i);
}, explode('.', $path));
foreach ($path as $p) {
if (! isset($arr[$p])) {
return null;
} else {
$arr = $arr[$p];
}
}
return $arr;
}
/**
* Sort multi-dimensional array using multiple predicates.
*
* @param array $data
* @param array $predicates
* @return array
*/
function msort(array $data, array $predicates): array
{
$predicatesArr = [];
foreach ($predicates as $predicate => $order) {
$predicatesArr[$predicate] = [];
foreach ($data as $i => $row) {
if (strstr($predicate, '.') !== false) {
$valueAtPath = array_dot_notation($row, $predicate);
} else {
$valueAtPath = $row[$predicate] ?: null;
}
$predicatesArr[$predicate]['_' . $i] = strtolower($valueAtPath);
}
if (! count($predicatesArr[$predicate])) {
unset($predicatesArr[$predicate]);
unset($predicates[$predicate]);
}
}
if (empty($predicatesArr)) {
return $data;
}
$eval = 'array_multisort(';
foreach ($predicates as $predicate => $order) {
$eval .= '$predicatesArr[\'' . $predicate . '\'],' . $order . ',';
}
$eval = substr($eval, 0, -1) . ');';
eval($eval);
$output = [];
foreach ($predicatesArr as $predicate => $predicateArr) {
foreach ($predicateArr as $i => $v) {
$i = substr($i, 1);
if (! isset($output[$i]))
$output[$i] = $data[$i];
}
}
return $output;
}
@cusster

This comment has been minimized.

Copy link
Owner Author

@cusster cusster commented Nov 12, 2018

A simple solution to sort multi-dimensional array using multiple sort criteria/properties, which may be in the form of dot notation. The direction of sort can also be defined per criterion/property.

Sample usage:

$data = [
    [
        "id"        => 1,
        "product"   => "iPhone X",
        "price"     => [
            "retail"    => 699.99,
            "sale"      => 599.99
        ]
    ],
    [
        "id"        => 2,
        "product"   => "Google Pixel 3",
        "price"     => [
            "retail"    => 799.99,
            "sale"      => 399.99
        ]
    ],
    [
        "id"        => 3,
        "product"   => "Samsung Galaxy Note 3",
        "price"     => [
            "retail"    => 699.99,
            "sale"      => 549.99
        ]
    ],
];

$sortCriteria = [
    "price.sale"    => SORT_ASC,
    "price.retail"  => SORT_DESC,
    "product"       => SORT_ASC
];

$sorted = msort($data, $sortCriteria);
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.