7

I need a function in php to sort a list of words according to an arbitrary ordering.

Any words in the list not in my predefined order should be sorted alphabetically at the end of the list.

Below is my first attempt, it is neither elegant or efficient. Can you suggest a better way to acheive this?

Thanks

public static function sortWords(&$inputArray){
    $order=array("Banana","Orange", "Apple", "Kiwi");
    sort($inputArray);
    for($i=0;$i<count($inputArray));$i++){
        $ac = $inputArray[$i];
        $position = array_search($ac,$order);
        if($position !== false && $i != $position){
            $temp=$inputArray[$position];
            $inputArray[$position]=$inputArray[$i];
            $inputArray[$i]=$temp;
        }
    }
}
Chris
  • 601
  • 1
  • 6
  • 4
  • You could sort both lists first (NlogN + MlogM time), then walk through the lists for matches (N+M time). Since you have to sort anyways, this is optimal. –  Apr 12 '11 at 08:40
  • I know it doesn't matter for your question, but you have an error in the `for` statement, two closing parenthesis instead of one for `$i – AJJ Apr 12 '11 at 08:44

4 Answers4

13

PHP provides the usort() and uksort() functions to allow you to write your own sorting routines. Of these two, you'll be wanting usort().

Both these functions expect you to write a stand-alone function, which takes as input two elements of the input array, and returns the order that they should be sorted into. The usort() function then runs its own sorting algorithm, calling your function as to establish the sorting order often as required until it has sorted the entire array.

So you'd write something like this....

function mycompare($a, $b) {
    if ($a == $b) {return 0;}
    $order=array("Banana","Orange", "Apple", "Kiwi");
    $position = array_search($a,$order);
    $position2 = array_search($b, $order);

    //if both are in the $order, then sort according to their order in $order...
    if ($position2!==false && $position!==false) {return ($position < $position2) ? -1 : 1;}
    //if only one is in $order, then sort to put the one in $order first...
    if($position!==false) {return -1;}
    if($position2!==false) {return 1;}

    //if neither in $order, then a simple alphabetic sort...
    return ($a < $b) ? -1 : 1;
}

...then just call usort($inputarray,'mycompare'); to sort them.

Spudley
  • 166,037
  • 39
  • 233
  • 307
1
public static function sortWords($inputArray){
    $order=array("Banana","Orange", "Apple", "Kiwi");
    $sorted_array = array_diff($inputArray,$order);
    sort($sorted_array);
    $rest_array = array_intersect($order,$inputArray);    
    $result = array_merge($rest_array,$sorted_array);
    return $result;
}

Haven't tested but try this.

Headshota
  • 21,021
  • 11
  • 61
  • 82
1

Probably slower than Headshota's solution, but just to provide you another (not tested) possibility:

function sortWordsCmp($a, $b) {
  $order=array("Banana","Orange", "Apple", "Kiwi");
  $a = array_search($a, $order);
  $b = array_search($b, $order);

  if ($a === $b)
    return 0;

  return (($b===false) || ($a < $b)) ? -1 : +1;
}

public static function sortWords($inputArray){
  usort($inputArray, 'sortWordsCmp');
  return $inputArray;
}
Udo G
  • 12,572
  • 13
  • 56
  • 89
0
public static function sortByArbitraryKeys(&$inputArray, $sort_order) {
    $sort_order = array_flip($sort_order);
    uksort($inputArray, function ($a, $b) use ($sort_order) {
        return $sort_order[$a] - $sort_order[$b];
    }
}

So an example would be the following...

// Doe, John L.
$this->full_name = ['last_name'=>'Doe', 'first_name'=>'John', 'middle_initial'=>'L.'];

// John L. Doe
$this->sortByArbitraryKeys($this->full_name, ['first_name', 'middle_initial', 'last_name']);

You could easily refactor this for whatever your specific use case is.

kjg61pt
  • 116
  • 6