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' );
有帮助吗?

解决方案

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];
});

其他提示

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
)
<?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"
}

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top