0

TL;DR: I need to sort an array of specific words. The order should match an existing array. See the example code in the last code block.

I have an array of titles, the first word of each title is a color. I'm used to sorting arrays at this point, but this one is confusing.

The problem: I need to manually sort an array of words based on a list of words that is already sorted. I have uasort set up so that I have the following two variables ready for the comparison:

// first comparison:
$a_first = strtolower( $a_split[0] ); // white
$b_first = strtolower( $b_split[0] ); // blue

// 2nd comparison:
$a_first = strtolower( $a_split[0] ); // purple
$b_first = strtolower( $b_split[0] ); // white

// 3rd comparison:
$a_first = strtolower( $a_split[0] ); // blue
$b_first = strtolower( $b_split[0] ); // purple

I need to sort these colors by belt ranking, for a jiu-jitsu ranking system. Here is the array in the correct order:

$color_order = explode(' ', 'white blue purple brown black black-red coral white-red red');

/* $color_order =
Array (
  [0] => white
  [1] => blue
  [2] => purple
  [3] => brown
  [4] => black
  [5] => black-red
  [6] => coral
  [7] => white-red
  [8] => red
) 

Current (incorrect) results:
1. Blue
2. Purple
3. White

Desired results:
1. White
2. Blue
3. Purple
*/

My current code, which comes out of uasort(), sorting alphabetically with strcmp. I need to replace strcmp with something that can sort using my color array. (Fyi, colors do not match the word, they are moved to a different array - so I don't need error checking in here).

// Sort Step 1: Sort belt level by color
// $video_categories[belt_id][term]->name = "White belt example"

function _sort_belt_names( $a, $b ) {
  $a_name = trim( $a['term']->name );
  $b_name = trim( $b['term']->name );

  $a_split = explode( ' ', $a_name );
  $b_split = explode( ' ', $b_name );

  if ( $a_split && $b_split ) {
    $color_order = explode(' ', 'white blue purple brown black black-red coral white-red red');

// IMPORTANT STUFF BELOW! ----
    $a_first = strtolower( $a_split[0] ); // purple
    $b_first = strtolower( $b_split[0] ); // white

    // Compare $a_first $b_first against $color_order
    // White should come first, red should come last
    // Return -1 (early), 0 (equal), or 1 (later)
// IMPORTANT STUFF ABOVE! ----
  }

  // If explode fails, sort original names alphabetically.
  return strcmp( $a_name, $b_name );
}

// ---

uasort( $video_categories, '_sort_belt_names' );
2
  • What is belt_id in $video_categories[belt_id]? Isn't it the color id? So you could sort by that.. Commented Jul 22, 2013 at 23:16
  • Nah, it's part of a larger system. Belt ID is from the database and is rather arbitrary. This system is to do the sorting, if there were already something sorted properly then it wouldn't be a problem ;) Commented Jul 22, 2013 at 23:20

4 Answers 4

1

Without seeing an example of the array, it's kind of hard to code, but something like this should work:

usort($arr, function ($a, $b){
    $ord =array_flip(array ('white', 'blue', 'purple', 'brown', 'black', 'black-red', 'coral', 'white-red', 'red'));
    return $ord[$b] - $ord[$a];
});
Sign up to request clarification or add additional context in comments.

1 Comment

Yeah, it's difficult to just "cut a piece out" for you guys on SO. I figured this would be pretty basic once you break it down. And I guess that was correct. Array flip worked fine! I knew the answer would be in the array keys for the colors, but I couldn't put my finger on it. Thanks
1

Here's a custom comparison function that uses a predefined order list.

function cmp($a,$b) {
   $order = array_flip(
      array(
         'white',
         'blue',
         'purple',
         'brown',
         'black',
         'black-red',
         'coral',
         'white-red',
         'red',
      )
   );
   $a = strtolower($a);
   $b = strtolower($b);
   if(!isset($order[$a])) {
      if(!isset($order[$b])) {
         return strcmp($a,$b);
      }
      return 1;
   }
   if(!isset($order[$b])) {
      return -1;
   }
   return $order[$a] - $order[$b];
}

Any 2 words that exist on the list are copared according to their order. If 1 word exists on the list and another doesn't, the latter is "lower". If both don't exist on the list, they're copared like any other pair of strings.

Use the function with usort like this:

$colors = array(
   'blue',
   'purple',
   'white',
   'foo',
   'bar',
);
usort($colors,'cmp');
print_r($colors);

You'll get:

Array
(
    [0] => white
    [1] => blue
    [2] => purple
    [3] => bar
    [4] => foo
)

Comments

1
<?php
 function _sort_belt_names( $a, $b ) {

  $a_split = explode( ' ', $a );
  $b_split = explode( ' ', $b );

  if ( $a_split && $b_split ) {
    $color_order = explode(' ', 'white blue purple brown black black-red coral white-red red');

// IMPORTANT STUFF BELOW! ----
    // Convert all the elements in $a_split and $b_split to lowercase

    foreach ( $color_order as $key => $value) {
        if ( in_array($value , $a_split) || in_array($value, $b_split)) {
           $sorted[] = $value;
        }

    }
    var_dump($sorted);
// IMPORTANT STUFF ABOVE! ----
  }

  // If explode fails, sort original names alphabetically.
  // Do whatever you whant to do if explode fails
}

$a = "black white coral blue";
$b = "black-red purple";
_sort_belt_names($a, $b);
?>

$ php sorting.php
array(6) {
  [0]=>
  string(5) "white"
  [1]=>
  string(4) "blue"
  [2]=>
  string(6) "purple"
  [3]=>
  string(5) "black"
  [4]=>
  string(9) "black-red"
  [5]=>
  string(5) "coral"
}

Comments

1

Not sure uasort is the right choice for this. How about checking your input array against the colours array (flipped) and order them by adding them to an array keyed on the flipped value (formerly the numeric key):

// Sort Step 1: setup color sort array(s)
$color_order = explode(' ', strtolower('white blue purple brown black black-red coral white-red red'));
$color_keyed = array_flip($color_order);

// Sort Step 2: Sort belt level by color
// $video_categories[belt_id][term]->name = "White belt example"

$unmatched=array();
$categorised=array();
$catsorted=array();
foreach($video_categories as $a) {

  $a_names = explode(' ',strtolower(trim( $a['term']->name)));
  $a_name=$a_names[0];
  // check if $a_name is a value in $color_order or a key in $color_keyed
  if (!array_key_exists("$a_name",$color_keyed) {
    $unmatched[]=$a;
  } else {
    // separated these 2 commands to make more readable but could have done:
    // $categorised[$color_keyed["$a_name"]][]=$a;
    // Putting the video_categories item into an array ordered by the key value of the matching color.
    $catkey=$color_keyed["$a_name"];
    $categorised[$catkey][]=$a;
  }
}

// You can now run a double foreach over the 2 dimensional $categorised array 
// to pick out the ordered video_categories entries:
// then write them to a single dimensional array
foreach ($categorised as $colkey=>$catkeyed)
{
  foreach ($catkeyed as $cat) {
  $catsorted[]=$cat;  
}
// ---
// $catsorted should now be a flat array 
// containing the original $video_categories array entries
// sorted by the order of the grade colors above

This should do it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.