generate every combination of variable multidimensional array.

BobsRe
BobsRe used Ask the Experts™
on
I am trying to generate every combination of variable multidimensional array. I have a class that generates an array similar to:
Array
(
    [0] => Array
        (
            [From] => 1248001200
            [To] => 1248030000
            [Staff] => 1
            [Employees] => Array
                (
                    [0] => 1367
                    [1] => 1360
                    [2] => 1361
                    [3] => 1375
                    [4] => 1403
                )

        )

    [1] => Array
        (
            [From] => 1248001200
            [To] => 1248030000
            [Staff] => 2
            [Employees] => Array
                (
                    [0] => 1378
                )

        )

    [2] => Array
        (
            [From] => 1248001200
            [To] => 1248087600
            [Staff] => 1
            [Employees] => Array
                (
                    [0] => 1367
                    [1] => 1360
                    [2] => 1361
                    [3] => 1375
                    [4] => 1403
                )

        )

    [3] => Array
        (
            [From] => 1248001200
            [To] => 1248087600
            [Staff] => 2
            [Employees] => Array
                (
                    [0] => 1357
                    [1] => 1378
                )

        )

    [4] => Array
        (
            [From] => 1248087600
            [To] => 1248116400
            [Staff] => 1
            [Employees] => Array
                (
                    [0] => 1367
                    [1] => 1360
                    [2] => 1361
                    [3] => 1375
                    [4] => 1403
                )

        ) ..........
It is variable length so I can't use a bunch of foreach's (at least not in any manor that I can think of. I am thinking that I many need to create a loop that calls a function that calls itself, but at this time I can't seem to get it.

Thanks in advance for any assistance.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
I'm not sure if I understand what you're trying to do. Can you break it down to a simple "This is some sample data and this is what I want in the end" ?

Commented:
Assume your array is named ar1 then try this:
foreach($ar1 as $value)
{
    echo $value[from];
    if (is_array($value)
    foreach($value as $innerValue)
   {
      echo $innerValue;
   {
}

Open in new window

Author

Commented:
I was thinking about this all night and I woke up this morning realizing that I didn't explain myself very well.

The time slots are shifts and I am looking to get every possible combination of employees in each time slot (from the Employees array with each time slot.) An example output would be (only using the top two arrays)

array (
[0] => array (
                        [0] => array ([From] => 1248001200, [To] => 1248030000, [Staff] => 1, [Employee] => 1367),
                        [1] => array ([From] => 1248001200, [To] => 1248030000, [Staff] => 2, [Employee] => 1378),
[1] => array (
                        [0] => array ([From] => 1248001200, [To] => 1248030000, [Staff] => 1, [Employee] => 1360),
                        [1] => array ([From] => 1248001200, [To] => 1248030000, [Staff] => 2, [Employee] => 1378)

[2] => array (
                        [0] => array ([From] => 1248001200, [To] => 1248030000, [Staff] => 1, [Employee] => 1361),
                        [1] => array ([From] => 1248001200, [To] => 1248030000, [Staff] => 2, [Employee] => 1378)


Although I only used the top two for this example output I want it to use the entire array. So if the array had four shifts in it each set would have every combination of all four. As you can see the secnond set only has one Employee listed so every possible outcome would only include that employee (1378)


Thanks
CompTIA Security+

Learn the essential functions of CompTIA Security+, which establishes the core knowledge required of any cybersecurity role and leads professionals into intermediate-level cybersecurity jobs.

Commented:
I lost, more explanation please? The whole matter please.

Commented:
Yeah, I'm still a little lost, too. I understand a little better, but it would still help if we knew some more info. Some questions:

1. Do you have an array of Employees (with no relation to shifts or anything - just a simple list of unique employee #s)

2. What does "Staff" mean (for example, does Staff = 2 mean that there is a quantity of 2 staff/employees on that shift, or simply indicating that the 2nd staff member is ####) ?

3. Are there any restrictions (example: an employee should not be on more than one shift per day) ?

If you're simply looking for permutations, that's one thing, but it sounds a little different, so I don't think that I understand yet.

Author

Commented:
[0] => array( // this combo id
         [0] => 1367 // The shift ID corresponding to the original array and the Employee ID
         [1] => 1378
         [2] => 1367
         [3] => 1357
         [4] => 1367),
   [1] => array( // this combo id
         [0] => 1367 // The shift ID corresponding to the original array and the Employee ID
         [1] => 1378
         [2] => 1367
         [3] => 1357
         [4] => 1360),
   [2] => array( // this combo id
         [0] => 1367 // The shift ID corresponding to the original array and the Employee ID
         [1] => 1378
         [2] => 1367
         [3] => 1357
         [4] => 1361),
   [3] => array( // this combo id
         [0] => 1367 // The shift ID corresponding to the original array and the Employee ID
         [1] => 1378
         [2] => 1367
         [3] => 1357
         [4] => 1375),
   [4] => array( // this combo id
         [0] => 1367 // The shift ID corresponding to the original array and the Employee ID
         [1] => 1378
         [2] => 1367
         [3] => 1357
         [4] => 1403),
   [5] => array( // this combo id
         [0] => 1367 // The shift ID corresponding to the original array and the Employee ID
         [1] => 1378
         [2] => 1367
         [3] => 1378
         [4] => 1367))

My finished array will of course not look like this as I will be doing other calculations, however for my question all I am looking for is how to accomplish the above with a variable number of shifts.

Thanks again.

Commented:
Not trying to be rude, but can you answer my 3 questions? The array you showed didn't really explain what I was trying to find out. Sometimes the biggest obstacle is fully explaining the problem (it makes sense to you, but remember that we don't know anything about this except for what you tell us).

Author

Commented:
Wow. I didn't realize that half my comment was cut off. Not sure what happened there was a whole paragraph before the example array. Let me retrace my thoughts.

Yes I do have an array of Employees, however the array that I am working with (as in my original question) has already filtered which employees are eligible for that shift (based on vacation, employee type, ... The original array has thousands of employees.)

Staff can be ignored. It is an ID of the employee type. It is used for other things and I can reference the original array to look up that information (same with From and To time)

As far as restrictions the array I am working with will already be filtered to some degree and I already have a function built to handle the rest of the restrictions (such as making sure they are not scheduled multiple shifts at the same time and about a dozen other things.) I will just call my function during the loop. The main thing I am stuck with is just how to iterate through a variable depth.

If it was constant I could:
foreach($Array[0]['Employees']  as $Employee) {
    foreach( $Array[1]['Employees']  as $Employee1) { ...

My first time around writing this response I wrote a whole ordeal about thanking you for asking detailed  questions because it makes a big difference, but I ended up posing without the answers. I'm sure I hit the touchpad on my laptop and highlighted all and wrote over it. It happens...

Thanks for being patient.

Author

Commented:
Im remembering more that I wrote before and lost ....

All I really need as shown in my last example it to iterate through and create every combination of shift to employees.

I think all the other data is over complication the question. All I really need is to accomplish what you see at http://www.phpfreaks.com/forums/index.php?topic=258770.0, but with a variable umber of wheels.

Commented:
Ah, I see, so the original source array has a variable length.

Out of curiousity, why not use nested foreach() then?

foreach($Array as $IDX => $SubArray)
{
   foreach($SubArray["Employees"] as $Employee)
   {
      ....
   }
}

Although I'm not sure how that ties into combinations. In your last array, you had a bunch of rows in the array that had duplicate employee IDs, so I'm not sure if that was intentional  or if you're looking for unique variations...

Author

Commented:
Each shift has a different set of employees (although they may be the same.) Which is why you see dupes, because the same ID may be in multiple lists (intentional.)

I would like to use a nested foreach, but I do not see how I can make a variable number of nests.

Commented:
You shouldn't need to make a variable number of nests unless the array has a variable depth. For example:

$Array[0][0] = "abc";
$Array[0][1] = "def";
$Array[1][0] = "ghi";
$Array[2][0] = "jkl";

That is not a variable depth - your array is two levels deep. The first level has 3 elements (0, 1, and 2), and the second level sometimes has two elements ([0][0], and [0][1]), and sometimes just one element ([1][0]). So in this case, you have a fixed depth of two levels, so you only need 2 foreach() statements to be able to go through everytthing.

A variable depth array looks like:

$Array[0][0] = "abc";
$Array[0][1][0] = "def";
$Array[1][0] = "ghi";
$Array[2] = "jkl";

In that case, sometimes the second level has another array inside it ([0][1][0]), and sometimes there's no second level at all ([2]).

If your original array has a fixed STRUCTURE (depth) - if it always looks like:

$array[0]["From"] = 123;
$array[0]["To"] = 234;
$array[0]["Staff"] = 2;
$array[0]["Employees"][0] = 1234;
$array[0]["Employees"][1] = 5678;

...then it doesn't matter how many of the structures you have. The first foreach() will go through the top level, like:

foreach($Shifts as $ShiftID => $Shift)
{
   foreach($Shift["Employees"] as $Employee)
   {
   ....
   }
}

The second foreach() will loop through however many Employees you have in that array. The length of the array doesn't matter to foreach() .

Author

Commented:
I may be misunderstanding, but I don't see how that will give me every possible combination. It will only go to each one once will it not?

Author

Commented:
If I have $Values => array ( array(1,2,3), array(1,2,3), array(1,2,3)));
foreach ($Values[0] as $Value0) {
   foreach ($Values[1] as $Value1) {
      foreach ($Values[2] as $Value2) {
         ...
      }
   }
}
would produce the right result as I know how many elements are in the main array's but ...

foreach ($Values as $Value) {
   foreach (Value as $Whatever) {
     ...
   }
}
Will not work.

The requested output should be
111
112
113
121
122
123
.........
Commented:
Hi BobsRe,

Sorry for the delay. Had some guests visiting for the weekend.

Now that I see a simplified example, I think I finally understand what you're trying to achieve and what you've been saying. I love those "Eureka" moments where everything suddenly clicks. Anyways, this is best achieved with a recursive function (a function that calls itself). If you haven't created recursive functions before, they can be a little confusing.

The concept behind it is to have a function that loops through the first "set" of the combination and then right before it ends, it takes off that first set and then calls itself with the remaining sets. Then, when the function runs again with the new "sets", it loops through one set, removes another set and keeps calling itself until all of the combinations have been tried.

Try looking at this snippet of code and see if it makes any more sense.
<?php
 
// Our initials
$Values = array(
	array(10,20,30),
	array(10,20,30,40),
	array(10,20,30),
	array(10,20,30,99)
);
 
// Create an array to store the combos
$Combinations = array();
 
getCombos("",$Values,$Combinations);
function getCombos($Combination,$Values,&$Combinations)
{
	if(count($Values))
	{
		$set = array_shift($Values);
		foreach($set as $el)
		{
			$NewCombination = $Combination . "," . $el; // Continue creating the next combination
			getCombos($NewCombination,$Values,$Combinations); // Recurse with our new combo
		}
	}
	else
	{
		$Combinations[] = substr($Combinationm,1); // Save combo without leading comma
	}
}
 
// Show the combos
print_r($Combinations);
 
?>

Open in new window

Author

Commented:
Thank you very much for your time. I ended up using what you see below .. after a long weekend that is what I came up with. It turns out I have to now go a different route anyway. After some calculations it turns out that for one of our examples it was going to take 27 hours to run, and that was not even close to the most complex customer we have. So we are going to have to run a cron to put combination's in the database and continue gradually. Any thoughts on the difference between your solution or mine? Either way you get the points. In my OP I stated "I am thinking that I many need to create a loop that calls a function that calls itself" I knew a recursive function was the key, just couldn't see it at first.

$Schedule = array(array());
if (count($Possibilities)) {
	foreach ($Possibilities as $ShiftID=>$Shift) {
		foreach ($Schedule as $ScheduleID=>$ThisSchedule) {
			foreach ($Shift['Employees'] as $Key=>$EmployeeID) {
				if (!$Key) {
					$Schedule[$ScheduleID][$ShiftID] = $EmployeeID;
				} else {
					$Schedule[][$ShiftID] = $EmployeeID;
				}
			}
		}
	}
}

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial