skip to Main Content

I am using the get_posts provided by WordPress to supply an array of post objects. A simplified version of this array is the following;

$zones = array
    0 => 
        object
            public 'post_title' => string 'Zone Five: Banana'
    1 => 
        object
            public 'post_title' => string 'Zone Eight: Banana'
    2 => 
        object
            public 'post_title' => string 'Zone Six: Banana'
    3 => 
        object
            public 'post_title' => string 'Zone Seven: Banana'
    4 => 
        object
            public 'post_title' => string 'Zone Four: Cherry'
    5 => 
        object
            public 'post_title' => string 'Zone Two: Orange'
    6 => 
        object
            public 'post_title' => string 'Zone Three: Avocado'
    7 => 
        object
            public 'post_title' => string 'Zone One: Apple'

As you can see, the post titles are not in order. Obviously an alphabetical sort won’t work.

Ideally, the first item of the array would be Zone One: Apple, and the last one being Zone Eight: Banana

I figured that usort would be the PHP function to use, but I can’t figure out how to compare substrings as opposed to an entire string.

Here is some code that works for Full text matching; (borrowed from another helpful stack overflow post)

$order = array_flip(['Zone One: Apple', 'Zone Two: Orange', 'Zone Three: Avocado', 'Zone Four: Cherry', 'Zone Five: Banana', 'Zone Six: Banana', 'Zone Seven: Banana', 'Zone Eight: Banana']); // restructure with values as keys, and keys as order (ASC)

$outlier = 1 + max($order);

usort($zones, function ($a, $b) use (&$order, $outlier) { // make $order modifiable with &
    if (!isset($order[$a->post_title])) {$order[$a->post_title] = $outlier;} // update lookup array with [id]=>[outlier number]
    if (!isset($order[$b->post_title])) {$order[$b->post_title] = $outlier;} // and again
        return $order[$a->post_title] <=> $order[$b->post_title];
});

Ideally, I’d like to sort by substring;

$order = [‘One’, ‘Two’, ‘Three’, ‘Four’, ‘Five’, ‘Six’, ‘Seven’, ‘Eight’];

2

Answers


  1. Take note that the code below meant to give you an example of what you can do. This code below will only work up to Ten. If you have eleven or nothing its behaviour could be undefined.

    For your issue, you need to parse number as words to actual int value and compare them. Thus, you need to somehow get the number as words out of the string. Thus, you need a dictionary of number as words to int values.

    It could be hard if you run a code that utilizes regexes the strings for every possible word that is a number. The other solution is if your title always starts with “zone” and then “number” then the “:”. You can split or substr or whichever method you feel efficient. That is the hard part. But after that, it is a breeze.

    One you found that number word, you convert it to int values and compare them and use the value appropriately.

    One thing to take into consideration is that what happens if one of the strings in the array does not contain a number?

    <?php
    $order = ['Zone One: Apple', 'Zone Two: Orange', 'Zone Three: Avocado', 'Zone Four: Cherry', 'Zone Five: Banana', 'Zone Six: Banana', 'Zone Seven: Banana', 'Zone Eight: Banana']; // restructure with values as keys, and keys as order (ASC)
    shuffle($order); // Shuffle the array
    
    // Acending = true                == smaller is smaller
    // Acending = false == descending == smaller is bigger
    function cmpStrWithNumberWord($str1, $str2, $ascending = true ){
        $numbers = ['one' => 1, 
                   'two' => 2,
                   'three' => 3,
                   'four' =>4,
                   'five' =>5,
                   'six' =>6,
                   'seven' =>7,
                   'eight' => 8,
                   'nine' => 9,
                   'ten' => 10];
    
        $regex = '/b(' . implode('|',array_keys($numbers)) . ')b/iU';
    
        $str1n = 0;
        $str2n = 0;
    
        preg_match($regex, $str1, $str1);
        preg_match($regex, $str2, $str2);
    
        if ( count($str1) > 1 )
            $str1n = $numbers[strtolower($str1[0])];
        if ( count($str2) > 1 )
            $str2n = $numbers[strtolower($str2[0])];
    
        if ( $ascending === true ){
            if ( $str1n === $str2n ) return 0;
            return $str1n < $str2n? -1 : 1;
        } else {
            if ( $str1n === $str2n ) return 0;
            return $str1n > $str2n? -1 : 1;
        }
    }
    
    var_dump($order); // Print the shuffled array.
    
    usort($order, 'cmpStrWithNumberWord' );
    var_dump($order); // Sorted array
    
    Login or Signup to reply.
  2. This does make assumptions that all of the data is structured how you say it is, as there isn’t any checks to ensure the data is of the right format. It also relies on PHP 7+ for small thinks like the spaceship operator (<=> ) in the comparison…

    $zones = ['Zone Seven: Banana', 'Zone Eight: Banana',
        'Zone Four: Cherry', 'Zone Five: Banana', 'Zone Six: Banana', 
        'Zone One: Apple', 'Zone Two: Orange', 'Zone Three: Avocado', 
        ];
    shuffle($zones);
    
    usort($zones, function ($a, $b) {
        static $sort = ['zero' => 0, 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4,
            'five' => 5, 'six' => 6, 'seven' => 7, 'eight' => 8, 'nine' => 9, 'ten' => 10
        ];
    
        $a1 = explode(" ", explode(":", $a)[0])[1];
        $b1 = explode(" ", explode(":", $b)[0])[1];
    
        return $sort[strtolower($a1)] <=> $sort[strtolower($b1)];
    });
    
    print_r($zones);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search