# Special Random Collection

**URL:** <https://forum.getkirby.com/t/special-random-collection/9454>\
**Category:** Questions\
**Tags:** v2\
**Created:** [January 13, 2018, 5:15pm UTC](https://forum.getkirby.com/t/special-random-collection/9454 "2018-01-13T17:15:14Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![culbrique](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/culbrique/32/3678_2.png) [@culbrique](https://forum.getkirby.com/u/culbrique)\
**Post date:** [January 13, 2018, 5:15pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/1 "2018-01-13T17:15:14Z")

</div>

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 ?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [January 14, 2018, 11:46am UTC](https://forum.getkirby.com/t/special-random-collection/9454/2 "2018-01-14T11:46:25Z")

</div>

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

```auto
  /**
   * 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

```auto
<?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 ?>

```

---

<div class="post-metadata">

**Author:** ![culbrique](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/culbrique/32/3678_2.png) [@culbrique](https://forum.getkirby.com/u/culbrique)\
**Post date:** [January 14, 2018, 12:11pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/3 "2018-01-14T12:11:39Z")

</div>

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 !

🎉🎉🎉🎉🎉🎉🎉

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [January 14, 2018, 5:30pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/4 "2018-01-14T17:30:39Z")

</div>

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

```auto
$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.

---

<div class="post-metadata">

**Author:** ![culbrique](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/culbrique/32/3678_2.png) [@culbrique](https://forum.getkirby.com/u/culbrique)\
**Post date:** [February 12, 2018, 1:55pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/5 "2018-02-12T13:55:05Z")

</div>

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 ?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [February 12, 2018, 6:51pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/7 "2018-02-12T18:51:42Z")

</div>

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

In fact, I had an error in the [first method](https://forum.getkirby.com/t/special-random-collection/9454/2?u=texnixe), which is now corrected, so that you can use it with `$min` set to 2 and you should be fine.

---

<div class="post-metadata">

**Author:** ![Oziris](https://avatars.discourse-cdn.com/v4/letter/o/df788c/32.png) [@Oziris](https://forum.getkirby.com/u/Oziris)\
**Post date:** [February 12, 2018, 7:10pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/8 "2018-02-12T19:10:36Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [February 12, 2018, 7:18pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/9 "2018-02-12T19:18:46Z")

</div>

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

```auto
  <?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?

---

<div class="post-metadata">

**Author:** ![Oziris](https://avatars.discourse-cdn.com/v4/letter/o/df788c/32.png) [@Oziris](https://forum.getkirby.com/u/Oziris)\
**Post date:** [February 12, 2018, 7:31pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/10 "2018-02-12T19:31:44Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [February 12, 2018, 7:45pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/11 "2018-02-12T19:45:01Z")

</div>

> [@Oziris](#):
>
> But I completely trust you !

😍

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.

---

<div class="post-metadata">

**Author:** ![Oziris](https://avatars.discourse-cdn.com/v4/letter/o/df788c/32.png) [@Oziris](https://forum.getkirby.com/u/Oziris)\
**Post date:** [March 6, 2018, 3:42pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/12 "2018-03-06T15:42:53Z")

</div>

Hi Sonja  
You already helped me a lot with your custom chunk methods to get random section and article with a minimum of 2 articles by sections.

My client asks me for something simple that I can’t do : always display the credits to the first position ! So my goal now is to display the first project from `page('projects')->children()->visible()` into the first « article » even if the page is refreshed.

How could I do an exception inside this foreach ?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [March 6, 2018, 3:54pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/13 "2018-03-06T15:54:36Z")

</div>

> [@Oziris](#):
>
> always display the credits to the first position

What do you mean by credits?

---

<div class="post-metadata">

**Author:** ![Oziris](https://avatars.discourse-cdn.com/v4/letter/o/df788c/32.png) [@Oziris](https://forum.getkirby.com/u/Oziris)\
**Post date:** [March 6, 2018, 3:58pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/14 "2018-03-06T15:58:07Z")

</div>

Ow, no matter, I would say this first page contains important information that I must display first despite of the random function

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [March 6, 2018, 4:39pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/15 "2018-03-06T16:39:55Z")

</div>

What I’d do:

- add a shuffle parameter to the method
- get the first item of the cloned collection (limit(1))
- define the rest as the cloned collection minus the first
- shuffle or not depending on parameter
- push the first item into the first chunk

---

<div class="post-metadata">

**Author:** ![Oziris](https://avatars.discourse-cdn.com/v4/letter/o/df788c/32.png) [@Oziris](https://forum.getkirby.com/u/Oziris)\
**Post date:** [March 6, 2018, 9:51pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/16 "2018-03-06T21:51:58Z")

</div>

I am not a PHP professional and I could never put that in place, but I feel (like the last time) that it looks more complicated than it seems.  
I was hoping to exclude the page with `->not()` like

`$projects = page('film')->children()->not('project-b')->visible()->shuffle();`

and added this page into the first position of the collection

```
Collection Object
(
    [0] => Children Object
        (
            [0] => film/project-b // Here my first project
            [1] => film/project-d
        )

    [1] => Children Object
        (
            [0] => film/project-e
            [1] => film/project-f
        )

)

```

I didn’t found a function to include a value in the first object of the collection, could be a solution?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [March 6, 2018, 11:46pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/17 "2018-03-06T23:46:20Z")

</div>

If you don’t want to do it in the function, why don’t you just print the first page first thing in your loop with a few if conditions?

---

<div class="post-metadata">

**Author:** ![Oziris](https://avatars.discourse-cdn.com/v4/letter/o/df788c/32.png) [@Oziris](https://forum.getkirby.com/u/Oziris)\
**Post date:** [March 7, 2018, 10:36am UTC](https://forum.getkirby.com/t/special-random-collection/9454/18 "2018-03-07T10:36:02Z")

</div>

Good question, I can’t tell you yet the true difference, but I think if the first page comes first, it doesn’t matter for me because it already exists a minimum of two articles into the first section.  
I would love to translate your idea but I’m afraid to don’t get it, could you provide me a simple example of this on a simple layout like this ?

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

---

<div class="post-metadata">

**Author:** ![distantnative](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/distantnative/32/11319_2.png) [@distantnative](https://forum.getkirby.com/u/distantnative)\
**Post date:** [February 6, 2025, 5:29pm UTC](https://forum.getkirby.com/t/special-random-collection/9454/19 "2025-02-06T17:29:52Z")

</div>

Archived on February 6, 2025, 5:29pm UTC


