8

I have an array of objects and I need to select 8 of them at random. My initial thought was to use array_rand(array_flip($my_array), 8) but that doesn't work, because the objects can't act as keys for an array.

I know I could use shuffle, but I'm worried about performance as the array grows in size. Is that the best way, or is there a more efficient way?

0

8 Answers 8

10
$result = array();
foreach( array_rand($my_array, 8) as $k ) {
  $result[] = $my_array[$k];
}
Sign up to request clarification or add additional context in comments.

1 Comment

This answer is missing its educational explanation.
8
$array = array();
shuffle($array); // randomize order of array items
$newArray = array_slice($array, 0, 8);

Notice that shuffle() function gives parameter as a reference and makes the changes on it.

1 Comment

@Chords You can bet that shuffle and array_slice contain a bit of looping. :)
3

You could use array_rand to pick the keys randomly and a foreach to gather the objects:

$objects = array();
foreach (array_rand($my_array, 8) as $key) {
    $objects[] = $my_array[$key];
}

Comments

1

I just found this in our code and was hoping to find a more readable solution:

$rand = array_intersect_key($all, array_flip(array_rand($all, $count)));

Comments

1

There are several ways to accomplish this task, but different approaches offer nuanced results. It makes no difference if the values are objects or any other data type. My demos will use a range of numbers from 1 to 9.

"shuffle & slice"

  • Concise and readable, but shuffle unnecessarily shuffles the full array despite needing only a subset.
  • Not a one-liner because shuffle() modifies by reference.
  • Result keys will be indexed and values will have a random order.

Code: (Demo)

shuffle($array);
var_export(array_slice($array, 0, 8));
//e.g.  [2, 8, 6, 9, 3, 5, 4, 1]

or

shuffle($array);
var_export(array_slice($array, -8));
//e.g.  [7, 3, 6, 8, 4, 1, 2, 5]


"array_rand (w/ count) & iterate"

  • Less concise, but still readable.
  • Can be written as a one-liner.
  • Result keys will be indexed and values will be ordered by their position in the input array.

Classic foreach: (Demo)

$result = [];
foreach (array_rand($array, 8) as $key) {
    $result[] = $array[$key];
}
var_export($result);
//e.g. [1, 2, 3, 4, 5, 6, 7, 8]

Functional-style one-liner: (Demo)

var_export(
    array_map(fn($k) => $array[$k], array_rand($array, 8))
);
//e.g. [1, 3, 4, 5, 6, 7, 8, 9]


"array_rand (w/ count) & flip & intersect_key"

  • Somewhat concise, less readable, calls 3 iterators instead of 2 like the aforementioned.
  • Can be written as a one-liner.
  • Result keys might not be indexed, but values will be ordered by their position in the input array.

Code: (Demo)

var_export(
    array_intersect_key($array, array_flip(array_rand($array, 8)))
);
//e.g. [1 => 2, 2 => 3, 3 => 4, 4 => 5, 5 => 6, 6 => 7, 7 => 8, 8 => 9]


"looped calls of array_rand"

  • Less concise, fairly readable, and makes iterated function calls.
  • Can be written as a one-liner, but calling range() is not super elegant.
  • Result keys will be indexed, but values will be unordered AND MAY OCCUR MORE THAN ONCE.

Classic foreach: (Demo)

$result = [];
for ($i = 0; $i < 8; ++$i) {
    $result[] = $array[array_rand($array)];
}
var_export($result);
//e.g.  [2, 5, 3, 1, 3, 5, 4, 2]

Functional-style (Demo)

var_export(
    array_map(fn() => $array[array_rand($array)], range(1, 8))
);
//e.g. [9, 2, 9, 9, 9, 5, 8, 8]

Comments

-1

What about?:

$count = count($my_array);
for ($i = 0; $i < 8; $i++) {
  $x = rand(0, $count);
  $my_array[$x];
}

1 Comment

This snippet was clearly not tested and doesn't work (doesn't do anything useful inside the loop body).
-1

You can get multiple random elements from an array with this function:

   function getRandomElements(array $array): array
    {
        $result = [];

        $count = count($array);
        for ($i = 0; $i < rand(0, $count); $i++) {
            $result[] = rand(0, $count);
        }
        $result = array_unique($result);
        sort($result);

        return $result;
    }

1 Comment

This is not what the question is asking for.
-1

You may try using this function to remix your object: this function takes your object and remixes it then returns it in a new order as it gets directly edited

you might also need to use a placeholder as a copy of the desired remix in case the original object wasn't edited.

/** randomize arrays and objects
 * @param mixed &$array an array taken to remix
 * @param int $swch - 1 if the first element is needed
 * @return mixed to store another copy of the remixed array
 */
function randomize(&$array, $swch = 0)
{
  // a placeholder for your array's first element
  if ($swch == 1) {
    $ST = $array[0];
    unset($array[0]);
  }
  // primary keys for the array or object to remix
  $k = array_keys($array);
  shuffle($k);

  /* note:- replace `$z[$v]` with `$z[]` incase you want to
   reorganize the array keys (numeral only) */
  foreach ($k as $v) {
    $z[$v] = $array[$v];
  }
  // restoring the remixed values
  $array = $z;
  if (isset($ST)) {
    array_unshift($array, $ST);
    unset($ST);
  }
  // launching a copy of the array incase needed
  return $array;
}

1 Comment

Question says: "I know I could use shuffle, but I'm worried about performance as the array grows in size." - Does your alternative address this in any way?

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.