Peter
Peter

Reputation: 9113

How to select randomly from array exluding one value?

With the holidays slowly nearing, It's time to pick straws again. We always pick a piece of paper from a box containing everbody's name. However, this year I wanted to solve the issue of picking your own name from the box by using PHP.

I've got an array with names ex:

$names = [
    'John',
    'Jane',
    'Joe',
    'Matt',
    'Steve',
    'Anne',
    'Karin'
];

For each of these names, I wanna pick 4 random names, so that at the end every person has 4 names for which they will have to buy a present.

The issue

Well, I'm stuck. I've thought of a million ways on how to do this but I just can't come up with anything.

What I've tried so far

The problem with these is that a person shouldn't be picking their own name and that everyone should be picked an even amount of times (4).

Update

Some great answers already posted. I'm not exactly sure though if this is fair for everyone. Everyone should at the end get 4 presents from 4 different persons.

I was thinking of adding each name to the $names array 4 times and then applying one of your answers. Am I on the right track with this?

Update 2

What I've tried now:

function select_rand($array, $exclude_array) {
    $diff = array_diff($array, $exclude_array);
    return $diff[array_rand($diff, 1)];
}

$workArray = [
    '0' => $names,
    '1' => $names,
    '2' => $names,
    '3' => $names,
];

foreach ($names as $k => $name) {
    $i = 0;
    $new[$name] = array();
    while ($i < 4) {
        $value = select_rand($workArray[$i], array_merge($new[$name], array($name)));
        if (($key = array_search($value, $workArray[$i])) !== false) {
            unset($workArray[$i][$key]);
        }
        $new[$name][] = $value;
        $i++;
    }
}

This works only in a few caes.

Upvotes: 2

Views: 84

Answers (4)

Mehdi
Mehdi

Reputation: 4318

I think if you want to have an even distribution of presents per person, you need to have more control over it. Anyhow, in this code I keep track of number of presents per person. I initialise it with zero: $presents = array_fill_keys($names, 0);. Then after each selection I updated this number $presents[$key]++;.

<?php
$names = array(
    'John',
    'Jane',
    'Joe',
    'Matt',
    'Steve',
    'Anne',
    'Karin'
);

$presents_number = 4;
// initialization 
$presents = array_fill_keys($names, 0);
$names_names = array();
foreach ($names as $i => $picker)  {
    $box = $names;
    unset($box[$i]);
    
    // filter out the people with maximum presents:
    foreach ($presents as $key => $number) {
        if (($presents[$key] > $presents_number-1) && in_array($key, $box)) {
            $box = array_diff($box, array($key));
        }
    }

    // shuffle the box and select 4 top
    shuffle($box);
    $selection = array_slice($box, 0, $presents_number);
    foreach ($selection as $key) {
        $presents[$key]++;
    }
    $names_names[$picker] = $selection;
}

echo "<pre>";
print_r($names_names);
echo "</pre>";

There can be more to be considered mathematically. Specially since loops can happen, this algorithm can go wrong. I haven't spend time on it. But as an example of 3 people and one present per person. (A, B, C)

correct answer:

A => {B}

B => {C}

C => {A}

wrong answer:

A => {B}

B => {A}

C => {}

Basically, the perfect algorithm should to avoid these wrong answers. Maybe later I fixed the problem as an update for this post.

Upvotes: 1

Sougata Bose
Sougata Bose

Reputation: 31739

This can help -

function select_rand($array, $exclude_array) {
    $diff= array_diff($array, $exclude_array);
    return $diff[array_rand($diff, 1)];
}

$names = array(
    'John',
    'Jane',
    'Joe',
    'Matt',
    'Steve',
    'Anne',
    'Karin'
);
foreach($names as $name)
{
    $i = 0;
    $new[$name] = array();
    while($i < 4) {
        $new[$name][] = select_rand($names, array_merge($new[$name], array($name)));
        $i++;
    }
}

This will generate a new array for each name (as key) in that array containing 4 unique names.

FIDDLE

Update

$new[$name][] = select_rand($names, array_merge($new[$name], array($name, 'Karin')));

Upvotes: 1

Narendrasingh Sisodia
Narendrasingh Sisodia

Reputation: 21437

You can create custom function like as

$names = [
    'John',
    'Jane',
    'Joe',
    'Matt',
    'Steve',
    'Anne',
    'Karin'
];

$my_name = "John";
$limit = 4;

function get_name($arr,$your_name,$limit){
    $key = array_flip($arr)[$your_name];
    unset($arr[$key]);
    $rand_keys = array_rand($arr,$limit);
    $result = array_intersect_key($arr, array_flip($rand_keys));
    return implode(',',$result);
}

echo get_name($names,$my_name,$limit);

Demo

Upvotes: 1

homelessDevOps
homelessDevOps

Reputation: 20726

I would use shuffle, and array_slice for this job:

$names = [
    'John',
    'Jane',
    'Joe',
    'Matt',
    'Steve',
    'Anne',
    'Karin'
];

foreach ($names as $name) {
   // working array
   $working = $names;
   // remove current name from array,no one wants to buy presents for him/her self..
   unset($working[$name]);
   shuffle($working);
   $people = array_slice($working, 0, 4);
   echo $name . ' has to buy presents for:';
   var_dump($people);
}

Upvotes: 3

Related Questions