I have this php Array
$stops = array('Pickup', 'Delivery', 'Pickup', 'Delivery');
What I want
Check each Pickup and combine it with next all Delivery. Above array should have 3 combinations index wise-
0-1
0-3
2-3
There can be any number of Pickup and Delivery and at any index.
What I tried
I first extracted all Pickup and all Delivery with below function-
public function getPickupDeliveryCombinations ($stops)
{
$all_pickups = array();
$all_delivaries = array();
$first_pickup_found = 0;
foreach($stops as $stop){
foreach($stop['location_stop_actions'] as $stop_action){
if($stop_action['stop_action_name']!=='Pickup' && $first_pickup_found == 0){
continue;
}else{
$first_pickup_found = 1;
if($stop_action['stop_action_name']=='Pickup'){
$all_pickups[] = $stop['load_location_name'];
}
if($stop_action['stop_action_name']=='Delivery'){
$all_delivaries[] = $stop['load_location_name'];
}
}
}
}
return($this->combinations(
array( $all_pickups, $all_delivaries, )
));
}
public function combinations($arrays, $i = 0)
{
if (!isset($arrays[$i])) {
return array();
}
if ($i == count($arrays) - 1) {
return $arrays[$i];
}
$tmp = $this->combinations($arrays, $i + 1);
$result = array();
foreach ($arrays[$i] as $v) {
foreach ($tmp as $t) {
$result[] = is_array($t) ?
array_merge(array($v), $t) :
array($v, $t);
}
}
return $result;
}
Then passed in function combinations, but issue in this is that It combines previous Delivery from a Pickup also.
I hope I explained well.
I can explain more if needed.
4
Answers
Makes sense to use a
for
loop here to iterate over the array, because then we can easily iterate over "the rest" of the entries after a found pickup, simply by using the next position as starting index for the inner loop.Output:
The first step of this solution is to build a mapping like this:
Setting the ‘Delivery’ entries to true and not the ‘Pickup’ entries allows for
array_filter
to be used without supplying a closure asarray_filter
returns all entries which evaluate to true.In the second step the loop acts on each ‘Pickup’ entry by checking !$isDelivery and then filters the keys/indices of the ‘Delivery’ entries. The use of
array_slice
speeds things up when the input array is large.For large input arrays – I tested with 1000 and 2000 random entries – this is a bit faster than the other solutions.
One thing to speed this up (possibly) is to first split the deliveries and pick ups into 2 lists, this saves having to repeatedly test the values in the array to check what sort it is. Then storing the positions in 2 arrays just means looping over the one and print out all of the remaining items in the other (discarding any ones in the deliveries which become less than the pickup)..
A slightly more complex but more efficient way to achieve your goal would be this:
First you declare a function that group your array keys by value like this
if you call this function on your array you get something like this