Special Random Collection


#1

Hello

I would like to make a random content bewteen 1 and n (max project entries)
But my request is quite special, let me explain, here is my foreach loop :

<?php foreach($site->find('projects')->children()->visible() as $project): ?>
    <section>
        <article>
            <?php echo $project->title()->html() ?>
        </article>
    </section>
 <?php endforeach ?>

I obtain :

<section>
    <article>
        Project 1
    </article>
</section>
<section>
    <article>
        Project 2
    </article>
</section>
<section>
    <article>
        Project 3
    </article>
</section>


What I’m looking for is something like

<section>
    <article>
        Project 1
    </article>
</section>
<section>
    <article>
        Project 3
    </article>
    <article>
        Project 2
    </article>
</section>

Or even :

<section>
    <article>
        Project 3
    </article>
    <article>
        Project 1
    </article>
    <article>
        Project 2
    </article>
</section>

This means that the random function I want to have could decide how many article or section to choose and displayed it according to the total number of the project pages.
Do you have an idea ?


#2

I created this custom pages method to create chunks of random size. Save it in a plugin file, e.g. site/plugins/methods.php.

  /**
   * Creates chunks of random size
   *
   * @param int $min Minimum number of items in chunk
   * @param int $max Maximum number of items in chunk
   * @return object A new collection with an item for each chunk and a subcollection in each chunk
   * Usage:
   * $projects = page('projects')->children()->shuffle();
   * $max = mt_rand(1,$projects->count());
   * $min = mt_rand(1, $max);
   * $chunks = $projects->randomChunk($min,$max);
   */
pages::$methods['randomChunk'] = function($pages, $min, $max) {
  $temp = clone $pages;
  $chunks = [];
  $size=1;
  ($min >=1 && $min <= $max)? $min=$min:$min=1;
  $max = $max <= $pages->count()? $max: $pages->count();
  while ($temp->count() > 0) {
    $size = mt_rand($min,$max);
    array_push($chunks, array_splice($temp->data,0, $size));
  }
  $chunkCollections = [];
  foreach($chunks as $items) {
    $collection = clone $pages;
    $collection->data = $items;
    $chunkCollections[] = $collection;
  }
  // convert the array of chunks to a collection object
  return new Collection($chunkCollections);
};

Use in your template

<?php
$projects = page('projects')->children()->visible()->shuffle();

/**
 **create random $min and $max values;
 ** these set the range for the random $size variable within the randomChunk() method; 
 ** instead of random values, you can also set one or both parameters to a fixed value, 
 ** depending on what sorts of results you want to get
*/
$max = mt_rand(1,$projects->count());
$min = mt_rand(1, $max);

$chunks = $projects->randomChunk($min,$max);
// $chunks is a nested collection, so we need two foreach loops to get the chunks and then all items in each chunk

foreach($chunks as $items): ?>
  <section>
    <?php foreach($items as $project): ?>
    <article class="">
      <?= $project->title()?>
    </article>
    <?php endforeach ?>
  </section>
  <hr>
<?php endforeach ?>

#3

WOAW Fantastic !!!
I felt that my question was too complicated, or not logical … but this function works incredibly well!
Thanks also for the comments that explain very well.
I hope it can be used by other people.

Bye, have a nice sunday !

:tada::tada::tada::tada::tada::tada::tada:


#4

Come to think of it, the same can be done with mapping and grouping:

$projects = page('projects')->children()->visible();
$max = mt_rand(1,$projects->count());
$min = 1; // can be random as well

$callback = function($p) {
  return $p->random;
};
$chunks = $projects->shuffle()->map(function($p) use($min,$max){
    $p->random = mt_rand($min,$max);
    return $p;
})->group($callback);


foreach($chunks as $section): ?>
  <section>
    <?php  foreach($section as $value): ?>
    <article class="">
      <?= $value->title()?>
    </article>
    <?php endforeach ?>
  </section>
<hr>
<?php endforeach ?>

Don’t know what is more performant.

Edit: Looks like the first version is faster than the second method, at least in my tests with 100 and something pages.


#5

Hi,
I could use the first method you send me, and it’s working perfeclty. After some use, I realized I also have to make another condition : I would like to have a minimum of 2 articles inside each sections.

I tried a lot a things, but I don’t really know how to write this. Could you help me with that ?


#7

Forget everything I said in the deleted post, I didn’t think.

In fact, I had an error in the first method, which is now corrected, so that you can use it with $min set to 2 and you should be fine.


#8

Really perfect, thank you so much.
Hope to share you as soon as possible my project.


#9

It’s not really perfect, it needs another modification because the last section can still have only one item

  <?php

  pages::$methods['randomChunk'] = function($pages, $min, $max) {
  $temp = clone $pages;
  $chunks = [];

  ($min >=1 && $min <= $max)? $min=$min:$min=1;
  $max = $max <= $pages->count()? $max: $pages->count();
  while ($temp->count() > 0) {
    $count = $temp->count();
    $size = mt_rand($min,$max);
    if($count - $size >= $min) {
      array_push($chunks, array_splice($temp->data,0, $size));
    } else {
      array_push($chunks, array_splice($temp->data,0, $count));
    }

  }
  $chunkCollections = [];
  foreach($chunks as $items) {
    $collection = clone $pages;
    $collection->data = $items;
    $chunkCollections[] = $collection;
  }
  // convert the array of chunks to a collection object
  return new Collection($chunkCollections);
};

How many accounts do you use?


#10

I didn’t saw the last article in your previous method could generate only one item…For now, I can’t say your second method is better : I have to continue to refresh the page to confirm that there are at least two articles. But I completely trust you !
And sorry for the confusion of accounts, I use one for personal projects, and the other for the professional : it’s easier to find topics.


#11

:heart_eyes:

It helps to test with only a few projects, then you see this immediately. What the last version does is check if the difference between the random number and the current item count is equal to or greater than $min(edited), and if not, it adds all current items to the last chunk.