1

This question is different than others, as it's focus is on sorting an array with a static class method rather than the typical procedural approach.

I am look for a very performant way to implement the function sortByKeyValue below. Other somewhat related answers are focused on getting the job done, this question is more about getting the job done and getting it done very quickly (as a static method).

Anyone want to take a crack at it? I'll probably throw some bounty on this question to squeeze out the performance junkies. :)

<?php

$data = array(
   array('name' => 'B', 'cheesy' => 'bacon'),
   array('name' => 'C', 'delicious' => 'tacos'),
   array('name' => 'A', 'lovely' => 'viddles'),
);

class MyArray {

    public static function sortByKeyValue($array, $key, $direction = 'ASC') {
        // Do the thing
        // Help me!

        return $array;
    }

}

$data = MyArray::sortByKeyValue($data, 'name');

// Should output the name key in order
// I am not including the garbage keys I threw in, but they should be there too)
// [{"name": "A"},{"name": "B"},{"name": "C"}]
?>
Kirk Ouimet
  • 27,280
  • 43
  • 127
  • 177

3 Answers3

3

You can use usort with a closure (Available php 5.3+)

 usort($array, function($a, $b){ return strcmp($a["name"], $b["name"]);});

Pre 5.3 you would have to create a sorter function and pass to usort

 function arraySorter($a, $b){
     return strcmp($a["name"], $b["name"]);
 }
 usort($array, "arraySorter");

Or you can place the arraySorter as a static method on your class

 public static function _arraySorter($a, $b){
     return strcmp($a["name"], $b["name"]); 
 }
 // then call
 usort($array, array("MyArray", "_arraySorter")); // for static 

Note. This will perform an in place sort.

As Suggested by @Kirk in comments you can use a function to return an anonymous sorter function so the implementation isn't bound to the name property

function getSorter($property){
       return function($a, $b) use ($property){
           return strcmp($a[$property], $b[$property]);
       };
}

Then you can call

usort($array, getSorter("name")); 
Orangepill
  • 24,500
  • 3
  • 42
  • 63
1

I ran the following to compare the speed of multisort, a pre-PHP 5.3 method, with a more modern method that uses usort with a closure function:

$alpha = 'abcdefghijklmnopqrstuvwxyz';
$cnt = 1000;
$key = 'name';
$direction = 'ASC';
$array = array();
for ($i=0; $i<$cnt; $i++){
    $array[$i]['name'] = substr(str_shuffle($alpha), 0, 8);
    $array[$i]['job'] = substr(str_shuffle($alpha), 0, 8);
}
$pre = $array;//the test dummies

//PRE-PHP 5.3

$t[0] = -microtime();
$sub = array();
foreach ($pre as $item) {
        $sub[] = $item[$key];
}
if ($direction == 'ASC') $ord = SORT_ASC;
else $ord = SORD_DESC;
array_multisort($sub, $ord, $pre);
$t[0] += microtime();

//USORT WITH CLOSURE

$t[1] = -microtime();
usort($array, function ($a, $b) use($key, $direction){
    if ($direction == 'ASC'){
        return strcmp($a[$key], $b[$key]);
    }
    return strcmp($b[$key], $a[$key]);
});
$t[1] += microtime();
var_dump($t);

As you can see, the old method was more than twice as fast:

Array
(
    [0] => 0.005
    [1] => 0.014001
)

So here's how I would do the class:

class MyArray {

    public static function sortByKeyValue($array, $key, $direction = SORT_ASC) {
        $sub = array();
        foreach ($array as $item) {
            $sub[] = $item[$key];
        }
        array_multisort($sub, $direction, $array);
        return $array;
    }

}
Expedito
  • 7,771
  • 5
  • 30
  • 43
1

I prefer array_multisort():

<?php
$data = array(
   array('name' => 'B', 'cheesy' => 'bacon'),
   array('name' => 'C', 'delicious' => 'tacos'),
   array('name' => 'A', 'lovely' => 'viddles'),
);

class MyArray {

    public static function sortByKeyValue($array, $key, $direction = 'ASC') {

        $tmp = array();
        foreach($array as $k=>$r){
            $tmp[] = $r[$key];
        }
        array_multisort($tmp,($direction == 'ASC' ? SORT_ASC : SORT_DESC),$array);

        return $array;
    }

}

$data = MyArray::sortByKeyValue($data, 'name');

echo '<pre>',print_r($data),'</pre>';
Samuel Cook
  • 16,620
  • 7
  • 50
  • 62