10

I want to select a random value from a array, but keep it unique as long as possible.

For example if I'm selecting a value 4 times from a array of 4 elements, the selected value should be random, but different every time.

If I'm selecting it 10 times from the same array of 4 elements, then obviously some values will be duplicated.

I have this right now, but I still get duplicate values, even if the loop is running 4 times:

$arr = $arr_history = ('abc', 'def', 'xyz', 'qqq');

for($i = 1; $i < 5; $i++){
  if(empty($arr_history)) $arr_history = $arr; 
  $selected = $arr_history[array_rand($arr_history, 1)];  
  unset($arr_history[$selected]); 
  // do something with $selected here...
}
Alex
  • 66,732
  • 177
  • 439
  • 641
  • 1
    On a side note, http://www.nsftools.com/misc/DilbertRandom.gif – Kumar Jun 24 '11 at 17:28
  • 3
    Why don't you use `array_rand($arr_history, 4)` to get the array in a random order? This ensures the that you have 4 unique values. In the next iteration you get the array in a different order. – Felix Kling Jun 24 '11 at 17:29
  • The statement `unset($arr_history[$selected]);` seems to treat `$arr_history` as an associative array, since `$selected` is the value, not the index. That doesn't solve the problem, though. Unsetting `$selected` is supposed to remove it from the array, which implies that by the time the loop s done, the array is empty. Was that your intention? (I don't think so) – afaolek Jun 24 '11 at 17:34

9 Answers9

13

You almost have it right. The problem was the unset($arr_history[$selected]); line. The value of $selected isn't a key but in fact a value so the unset wouldn't work.

To keep it the same as what you have up there:

<?php

$arr = $arr_history = array('abc', 'def', 'xyz', 'qqq');

for ( $i = 1; $i < 10; $i++ )
{
  // If the history array is empty, re-populate it.
  if ( empty($arr_history) )
    $arr_history = $arr;

  // Select a random key.
  $key = array_rand($arr_history, 1);

  // Save the record in $selected.
  $selected = $arr_history[$key];

  // Remove the key/pair from the array.
  unset($arr_history[$key]);

  // Echo the selected value.
  echo $selected . PHP_EOL;
}

Or an example with a few less lines:

<?php

$arr = $arr_history = array('abc', 'def', 'xyz', 'qqq');

for ( $i = 1; $i < 10; $i++ )
{
  // If the history array is empty, re-populate it.
  if ( empty($arr_history) )
    $arr_history = $arr;

  // Randomize the array.
  array_rand($arr_history);

  // Select the last value from the array.
  $selected = array_pop($arr_history);

  // Echo the selected value.
  echo $selected . PHP_EOL;
}
Francois Deschenes
  • 24,816
  • 4
  • 64
  • 61
  • thanks that seems to work, but it's weird that a array like this has keys. I thought that both key and value would be the same – Alex Jun 24 '11 at 17:39
  • 2
    @Alex - No, in fact, when you don't specify the keys, PHP assigns them numeric keys starting at 0. See the parameters section of [http://ca3.php.net/manual/en/function.array.php](http://ca3.php.net/manual/en/function.array.php) for more information. – Francois Deschenes Jun 24 '11 at 17:43
  • It's alway the same result, no random – Gino Jul 20 '15 at 17:20
7

How about shuffling the array, and popping items off.

When pop returns null, reset the array.

$orig = array(..);
$temp = $orig;
shuffle( $temp );

function getNextValue()
{
  global $orig;
  global $temp;

  $val = array_pop( $temp );

  if (is_null($val))
  {
    $temp = $orig;
    shuffle( $temp );
    $val = getNextValue();
  }
  return $val;
}

Of course, you'll want to encapsulate this better, and do better checking, and other such things.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
4

http://codepad.org/sBMEsXJ1

<?php

    $array = array('abc', 'def', 'xyz', 'qqq');

    $numRandoms = 3;

    $final = array();

    $count = count($array);

    if ($count >= $numRandoms) {

        while (count($final) < $numRandoms) {

            $random = $array[rand(0, $count - 1)];

            if (!in_array($random, $final)) {

                array_push($final, $random);
            }
        }
    }

    var_dump($final);

?>
Steve Robbins
  • 13,672
  • 12
  • 76
  • 124
3

Php has a native function called shuffle which you could use to randomly order the elements in the array. So what about this?

$arr = ('abc', 'def', 'xyz', 'qqq');

$random = shuffle($arr);

foreach($random as $number) {
    echo $number;
}
KingOfCode
  • 31
  • 2
  • `shuffle` actually returns a boolean and modifies the original array so you would want `foreach ($arr as $a)` – MarcGuay Feb 15 '17 at 20:51
2

key != value, use this:

$index = array_rand($arr_history, 1);
$selected = $arr_history[$index];  
unset($arr_history[$index]); 
Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
1
$isShowCategory = array();
for ($i=0; $i <5 ; $i++) { 
   $myCategory = array_rand($arrTrCategoryApp,1); 
   if (!in_array($myCategory, $isShowCategory)) {
      $isShowCategory[] = $myCategory;

      #do something 

   }
}
lookly Dev
  • 325
  • 3
  • 5
  • adding some context around your answer (eg why it solves the problem) would help other people looking at the answer. – sbkrogers Jun 02 '20 at 20:54
1

I've done this to create a random 8 digit password for users:

$characters = array(
    "A","B","C","D","E","F","G","H","J","K","L","M",
    "N","P","Q","R","S","T","U","V","W","X","Y","Z",
    "a","b","c","d","e","f","g","h","i","j","k","m",
    "n","p","q","r","s","t","u","v","w","x","y","z",
    "1","2","3","4","5","6","7","8","9");

for( $i=0;$i<=8;++$i ){
    shuffle( $characters );
    $new_pass .= $characters[0];
}
sadmicrowave
  • 39,964
  • 34
  • 108
  • 180
1

If you do not care about what particular values are in the array, you could try to implement a Linear Congruential Generator to generate all the values in the array.

LCG implementation

Wikipedia lists some values you can use, and the rules to select the values for the LCG algorithm, because the LCG algorith is deterministic it is guaranteed not to repeat a single value before the length of the period.

After filling the array with this unique numbers, you can simply get the numbers in the array 1 by 1 in order.

Oscar Gomez
  • 18,436
  • 13
  • 85
  • 118
-1

Easy and clean:

$colors = array('blue', 'green', 'orange');
$history = $colors;

function getColor($colors, &$history){
    if(count($history)==0)
        $history = $colors;
    return array_pop( $history );
}

echo getColor($colors, $history);
Dave
  • 3,073
  • 7
  • 20
  • 33