Sort array of arrays by a column with custom sorting priority (not alphabetically)
Asked Answered
G

5

5

Let's say I have this array:

$array = array(
    array("id" => 7867867, "animal" => "Dog"),
    array("id" => 3452342, "animal" => "Lion"),
    array("id" => 1231233, "animal" => "Lion"),
    array("id" => 5867867, "animal" => "Dog"),
    array("id" => 1111111, "animal" => "Zeebra"),
    array("id" => 2222222, "animal" => "Cat"),
    array("id" => 3333333, "animal" => "Cat"),
    array("id" => 4444444, "animal" => "Zeebra")
);

Now what I've been trying to do is use php sort functions to be able to sort it based on specific rules (not alphabetical)

The client wants this information sorted by "Lion first, Dog second, Zeebra third, Cat fourth".

Something like this:

$array = array(
    array("id" => 3452342, "animal" => "Lion"),
    array("id" => 1231233, "animal" => "Lion"),
    array("id" => 7867867, "animal" => "Dog"),
    array("id" => 5867867, "animal" => "Dog"),
    array("id" => 4444444, "animal" => "Zeebra"),
    array("id" => 1111111, "animal" => "Zeebra"),
    array("id" => 2222222, "animal" => "Cat"),
    array("id" => 3333333, "animal" => "Cat"),
);

The array would be sorted using the "animal" value and would be based on pre-determined rules.

I was trying to figure out php sort functions but I could only get those to work with sorting the arrays alphabetically or numerically.

What I've gotten to work is a block of if-statements and loops, and I would like to get rid of that slow code as soon as I can.

Grain answered 28/4, 2014 at 22:18 Comment(0)
P
7

Using Jonathan suggestion on using usort, you can define your custom rules for sorting in a separate function, like:

function getAnimalValue($animal) {
    switch($animal) {
        case 'Lion':
            return 1;
        case 'Dog':
            return 2;
        case 'Zeebra':
            return 3;
        case 'Cat':
            return 4;
    }
    return 0;
}

Then, implement your own compare function:

function compare($itemA, $itemB) {
    $a = getAnimalValue($itemA['animal']);
    $b = getAnimalValue($itemB['animal']);
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

Finally, call usort using the compare function:

usort($array, "compare");
Parathion answered 28/4, 2014 at 22:37 Comment(0)
H
3

PHP7 offers the "spaceship operator" to make sorting much, much simpler.

All of the following snippets will leverage a lookup array to determine the custom sorting order.

$priorities = array_flip(['Lion', 'Dog', 'Zeebra', 'Cat']);

Code: (Demo)

usort($array, function($a, $b) use ($priorities) {
    return $priorities[$a['animal']] <=> $priorities[$b['animal']];
});

PHP7.4+ Code: (Demo)

usort($array, fn($a, $b) => $priorities[$a['animal']] <=> $priorities[$b['animal']]);

Your question and sample data suggest that you always have all animals acknowledged in your lookup array. If this is not always true, then you will need to make isset() calls in your custom function and determine if you want missing animals to be placed at the front or back of the list.

Both snippets above provide the same output:

array (
  0 => 
  array (
    'id' => 3452342,
    'animal' => 'Lion',
  ),
  1 => 
  array (
    'id' => 1231233,
    'animal' => 'Lion',
  ),
  2 => 
  array (
    'id' => 7867867,
    'animal' => 'Dog',
  ),
  3 => 
  array (
    'id' => 5867867,
    'animal' => 'Dog',
  ),
  4 => 
  array (
    'id' => 1111111,
    'animal' => 'Zeebra',
  ),
  5 => 
  array (
    'id' => 4444444,
    'animal' => 'Zeebra',
  ),
  6 => 
  array (
    'id' => 2222222,
    'animal' => 'Cat',
  ),
  7 => 
  array (
    'id' => 3333333,
    'animal' => 'Cat',
  ),
)
Hyperplane answered 10/12, 2019 at 8:25 Comment(0)
P
2

Check usort. Heres the reference:

http://www.php.net/manual/en/function.usort.php

Example:

function cmp($a, $b) {
    $order=array("Lion","Dog","Zebra","Cat");
    if ($a["animal"] == $b["animal"]) {
        return 0;
    }
    return (array_search($a["animal"],$order) < array_search($b["animal"],$order)) ? -1 : 1;
}

$array = array(
    array("id" => 7867867, "animal" => "Dog"),
    array("id" => 3452342, "animal" => "Lion"),
    array("id" => 1231233, "animal" => "Lion"),
    array("id" => 5867867, "animal" => "Dog"),
    array("id" => 1111111, "animal" => "Zebra"),
    array("id" => 2222222, "animal" => "Cat"),
    array("id" => 3333333, "animal" => "Cat"),
    array("id" => 4444444, "animal" => "Zebra")
);

$mySortedArray=usort($array, "cmp");
Prospect answered 28/4, 2014 at 22:21 Comment(3)
Could you give an example on how I would use this to compare string values to the user-defined rules?Grain
This is a good example, but now you can use the space ship operator <=> to make the code simpler usort($animals, function ($a, $b) { $order = ['Dog', 'Lion', 'Zeebra', 'Cat']; return (array_search($a["animal"],$order) <=> array_search($b["animal"],$order)); }); Sorry I am not sure how to do new lines in a commentLofty
@JasonBruce, yes, this answer was provided in 2014 before spaceship was added to PHP. Feel free to update the answer.Prospect
P
0

This is a bit of an ugly way, but it works.

// Set our rules, the key is the order
$rules = array(0=>"Dog",1=>"Lion",2=>"Zeebra",3=>"Cat");

// Our array
$array = array(
    array("id" => 7867867, "animal" => "Dog"),
    array("id" => 3452342, "animal" => "Lion"),
    array("id" => 1231233, "animal" => "Lion"),
    array("id" => 5867867, "animal" => "Dog"),
    array("id" => 1111111, "animal" => "Zeebra"),
    array("id" => 2222222, "animal" => "Cat"),
    array("id" => 3333333, "animal" => "Cat"),
    array("id" => 4444444, "animal" => "Zeebra")
);

// Split each animal into a arrays per animal
foreach( $array as $item ){
    $animals[ $item['animal'] ][] = $item;
}

// Loop our rules (dogs, then lions, etc) and put our mini arrays in a final sorted_array.
foreach( $rules as $animal_order => $animal_name ){
    foreach( $animals[$animal_name] as $animal ){
        $sorted_animals[] = $animal;
    }

}

print_r($sorted_animals);
Parotid answered 28/4, 2014 at 22:36 Comment(0)
A
-1

Try:

function sortByAnimals($array, $animalOrderArray){
  foreach($animalOrderArray as $v){
    $aa[$v] = array();
  }
  foreach($array as $a){
    foreach($aa as $i => $v){
      if(preg_match("/^{$a['animal']}$/i", $i)){
        array_push($aa[$i], $a);
        break;
      }
    }
  }
  foreach($aa as $a){
    foreach($a as $v){
      $r[] = $v;
    }
  }
  return $r;
}
$resArray = sortByAnimals($array, array('lion', 'dog', 'zeebra', 'cat'));
Athletics answered 28/4, 2014 at 23:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.