1

I have two Custom Post Types and would like to display one of them sorted (by title) and the other one at random places in the same query.

So CPT A (sorted) en CPT B (random):

A1 A2 A3 B2 A4 A5 B5 A6 A7 A8 A9 B1 ...
or
A1 B1 A2 A3 B5 A4 A5 A6 A7 A8 A9 B7 ...
or
B4 A1 A2 A3 A4 B3 A5 A6 A7 A8 B1 A9 ...

I tried merging the two CPT but that didn't work as expected.

$projectposts = get_posts(array(
 'posts_per_page' => -1,
 'post_type'      => 'project',
 'order'          => 'ASC'
));
        
//second query
$storieposts = get_posts(array(
 'posts_per_page' => -1,
 'post_type'      => 'storie',
 'order'          => 'rand'
));
        
$mergedposts = array_merge( $projectposts, $storieposts ); //combined querie

@kero:
I have this ↓ and it's almost working... It looks like this creates some empty entries in the array. (I'm 100% certain that there are no empty titles before merging.)
The query results in A3 A2 A4 A6 B2 (empty) (empty) (empty) A1 (empty) (empty) B1 B3 ...

   $projectposts = get_posts([
     'posts_per_page' => -1,
     'post_type'      => 'project',
     'order'          => 'ASC',
     'meta_query'     => array(
        array(
          'key'     => 'featured',
          'value'   => '"homepagina"',
          'compare' => 'LIKE'
        )
      )
   ]);
   
   $storieposts = get_posts([
       'posts_per_page' => -1,
       'post_type'      => 'story',
       'order'          => 'ASC',// not rand!
       'meta_query'     => array(
          array(
            'key'     => 'featured',
            'value'   => '"homepagina"',
            'compare' => 'LIKE'
          )
        )
   ]);
   
   function array_random_merge(array $a, array $b): array {
       while (count($b) > 0) {
           shuffle($b);
           // get one entry of $b
           $item = array_shift($b);
           // find random position inside $a
           $pos = mt_rand(0, count($a) - 1);
           // insert $item into $a
           array_splice($a, $pos, 0, $item);
       }
       return $a;
   }
   
   $mergedposts = array_random_merge($projectposts, $storieposts);
   
    if( $mergedposts ) {
      foreach( $mergedposts as $post ) { 
        
        echo '<h3>';
        echo the_title();
        echo '</h3>';
        
      }
    }
    wp_reset_postdata(); 
    
6
  • 1
    Just a note that using the rand order is extremely heavy/slow/expensive, always do the random part in PHP where possible, as a random order can involve a full table copy into a temporary table that gets shuffled randomly, queried, then destroyed. It would be significantly faster to fetch all stories and randomly sort them after the get_posts call
    – Tom J Nowell
    Jan 13, 2021 at 12:28
  • In your example, is the following also fine B1 B2 B3 A1 A2 .. / A1 A2 B1 B2 A3 A4 B3 ..? If not, what are your other additional rules for the distribution of B?
    – kero
    Jan 13, 2021 at 14:38
  • B1 B2 B3 A1 A2 .. / A1 A2 B1 B2 A3 A4 B3... is fine too, but B should –preferably– be 'inserted' randomly.
    – zuperuser
    Jan 13, 2021 at 16:13
  • @kero I have updated my question (see above). The random placement and shuffle of 'B' is working correct, but it creates empty spaces (<h3></h3> in my case).
    – zuperuser
    Jan 14, 2021 at 8:09
  • @zuperuser Thanks for editing it into your question! I cannot reproduce your problem - so I'm still thinking there might be some problem with the WP methods in there. Can you create a fiddle somewhere (e.g. the site I used) that shows the problem?
    – kero
    Jan 14, 2021 at 9:54

2 Answers 2

1

Try to get two different arrays of posts and loop through array A. Before get post data of A element get random element from array B.

For example:

$array_a = ['post_id_a1', 'post_id_a2', 'post_id_a3'];
$array_b = ['post_id_b1', 'post_id_b2', 'post_id_b3'];

foreach ( $array_a as $a_post_id ) {
    $is_b_should_displayed = mt_rand( 0, 1 );

    if ( $is_b_should_displayed && $b_length = count( $array_b ) ){
        $b_to_show = mt_rand( 0, $b_length - 1 );

        // show random B post

        unset( $array_b[ $b_to_show ] );
        $array_b = array_values( $array_b ); // to reorder array after unset
    }

    // show A post
}

Or in same way create array C, and then loop through C to display posts. There is no warranty all B will displayed, so, if you need it, you could loop through B (if at least 1 element exist) after main A loop

Small note: rand using for key orderby not to order according to wp docs.

Hope it helps

3
  • 1
    First, use mt_rand() instead of rand() - second, here it could happen, that no entry from B is added, because by bad luck all your "should be displayed" are false. (Unlikely scenario, but can happen.)
    – kero
    Jan 13, 2021 at 14:29
  • 1
    @kero, thanks for your advice. I couldn't leave comments (not enough reputation) so write here: he should try use inside loop next foreach(){ setup_postdata($post); //other code }
    – burlakvo
    Jan 19, 2021 at 15:31
  • Well spotted! You should be able to comment now yourself btw ;)
    – kero
    Jan 19, 2021 at 17:17
0

I would probably do it like this:

$projects = get_posts([
    'posts_per_page' => -1,
    'post_type'      => 'project',
    'order'          => 'ASC',
]);

$stories = get_posts([
    'posts_per_page' => -1,
    'post_type'      => 'storie',
    'order'          => 'ASC',// not rand!
]);

function array_random_merge(array $a, array $b): array {
    while (count($b) > 0) {
        shuffle($b);
        // get one entry of $b
        $item = array_shift($b);

        // find random position inside $a
        $pos = mt_rand(0, count($a) - 1);
        // insert $item into $a
        array_splice($a, $pos, 0, $item);
    }
    return $a;
}

$merged = array_random_merge($projects, $stories);

The main idea here is the following:

  1. remove one $item from $b
  2. insert that at a random position in $a
  3. repeat 1 & 2 until $b is empty.

Per Tom's comment I removed rand order in the query and simple shuffle() the array.

0

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.