0

I'm wondering if there is a correct/easier way of doing this. I have an array of items, and I want to pick an amount from this array. The problem comes as I want to be able to pick more items than are actually in the array

I currently have the following

$cardsList = array_rand($this->specials, $amount);

this results in an error when $amount is larger than the amount in $this->specials

Warning: array_rand(): Second argument has to be between 1 and the number of elements in the array

I know I can use a for statement and pick 1 item at a time add add them to the array but is there a better/easier way?

for($i = 0; $i<$amount; $i++) {
    $cardsList[] = array_rand($this->specials, 1);
}
2
  • So what do you want to do? Limit the number of returned occurances or come up with a way of getting more results than occurances exists in the array? Commented Sep 29, 2018 at 12:46
  • What is wrong with your current approach? Commented Sep 29, 2018 at 13:01

4 Answers 4

1

Create $amount again with random number if it is more than $this->specials

$amount = $amount<=count($this->specials)?$amount: rand(0,count($this->specials));
$cardsList = array_rand($this->specials, $amount);
Sign up to request clarification or add additional context in comments.

Comments

0

You could just write a function that will do what you desire:

function getRandomArrayValues($arr, $amount){
   $result = array();
   for($i = 0; $i < amount; $i++){
      $result[$i] = array_rand($arr, 1);
   }
  return $result;
}

1 Comment

Is this not what the OP already suggested thay could do in their question?
0

Your approach is fairly common, since array_rand only returns an array of randomized keys of the supplied array, or returns a single random key from the array when supplying 1 as the amount, you will be forced to use a for loop to retrieve the desired amount, and leads to reduced performance when a very large amount is requested.

An alternative to array_rand is using shuffle. To resolve a desired amount larger than the source array, use array_merge to fill the array until the desired amount is reached. Then use array_slice to reduce the shuffled array to exactly the desired amount.

Example: https://3v4l.org/LDSj4

//create an array of 8 values - for this example
$a = range('A', 'H');

//specify a larger amount of values to retrieve
$amount = 20;
//this only occurs when the array is smaller than the amount
while (count($a) < $amount) {
    //fill the array exponentially with the same values until amount is reached
    $a = array_merge($a, $a);
}
//randomize the array order
shuffle($a);
//reduce the array to the desired amount
$a = array_slice($a, 0, $amount);
var_dump($a);

Example Results (results vary as seen in the example link);

array(20) {
  [0]=>
  string(1) "F"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "E"
  [3]=>
  string(1) "B"
  [4]=>
  string(1) "H"
  [5]=>
  string(1) "A"
  [6]=>
  string(1) "C"
  [7]=>
  string(1) "C"
  [8]=>
  string(1) "C"
  [9]=>
  string(1) "G"
  [10]=>
  string(1) "G"
  [11]=>
  string(1) "H"
  [12]=>
  string(1) "D"
  [13]=>
  string(1) "F"
  [14]=>
  string(1) "A"
  [15]=>
  string(1) "G"
  [16]=>
  string(1) "E"
  [17]=>
  string(1) "C"
  [18]=>
  string(1) "E"
  [19]=>
  string(1) "D"
}

Comments

0

@Ceri Turner unfortunately array_rand() will not accept $amount if it is larger than your source array count. You have to write custom function, you don't have any other in built function.

After looking into some suggestions from @fyrye I'd done some modification into the function as given. It can also randomize more efficiently than previous one.

You can check live example https://3v4l.org/tPc7A

<?php

function randomizer($sArray, $target) 
{
    $b = (count($sArray)/2); // Randomize count for better result make it half of the total array count
    $c = floor($target/$b); // Loop Count
    $r = $target-($b*$c); // Rest of the Count
    $cardsList = array();
    if($c > 0){
        /* Now Loop for Full Number */
        for($i=0; $i<$c; $i++){
            $cardsList = array_merge($cardsList,array_rand($sArray, $b));
        }
        if($r >= 1){
            $restArray = array_rand($sArray, $r);
            if(is_array($restArray)){
                $cardsList = array_merge($cardsList,$restArray);
            }else{
                $cardsList[] = $restArray;
            }
        }
    }else{
        $cardsList = array_merge($cardsList,array_rand($sArray, $target));
    }
    return $cardsList;
}

//only 8 values
$sArray = range('A', 'H');

$resultArray = randomizer($sArray, 67);
echo 'Amount 50 -  result: ' . count($resultArray) . \PHP_EOL; 
print_r($resultArray);
?>

5 Comments

$cardsList will be a multi-dimensional array (array of arrays). and still results in the error the OP experiences if the origin array length is less than $amount / 2 .ie. $this->specials is only 8. Warning: array_rand(): Second argument has to be between 1 and the number of elements in the array in /in/L193n on line 12
Example: 3v4l.org/ZDmDF As I stated if $arrCount < ($amount / 2) will result in the warning, since $restAmount will still be higher than $arrCount
Hi @Ceri Turner please check the code on link given by fyrye Example: 3v4l.org/ZneOv , I've updated it and it will give you desired result.
You should update your answer to reflect your corrections. However it appears to be much more complex than the OP's original usage and the results are very sequential since each use starts with [0,1,2,3,4,5] and are identical on several versions. 3v4l.org/EkJt9 it also fails with much larger amounts like 50 3v4l.org/Hurld
@fyrye I think it is not much complex function, also build in randomization is poor in array_rand function. So I checked with half of the total source array count for base randomization and result is much more likely to be desired. I'd updated my answer. You can also visit 3v4l.org/tPc7A

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.