skip to Main Content

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


  1. 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.

    $stops = array('Pickup', 'Delivery', 'Pickup', 'Delivery');
    
    $result = [];
    
    for($i=0, $c=count($stops); $i<$c; ++$i) {
        if($stops[$i] === 'Pickup') {
            for($j=$i+1; $j<$c; ++$j) {
                if($stops[$j] === 'Delivery') {
                    $result[] = $i . '-' . $j;
                }
            }
        }
    }
    
    print_r($result);
    

    Output:

    Array
    (
        [0] => 0-1
        [1] => 0-3
        [2] => 2-3
    )
    
    Login or Signup to reply.
  2. $stops = [ 'Pickup', 'Delivery', 'Pickup', 'Delivery' ];
    
    $result = [];
    
    $map = array_map(static fn($stop) => $stop === 'Delivery', $stops);
    
    foreach ($map as $pIndex => $isDelivery) {
      if (!$isDelivery) {
        $dIndices =
          array_keys(
            array_filter(
              array_slice($map, $pIndex + 1, preserve_keys: true)
            )
          );
        foreach ($dIndices as $dIndex) {
          $result[] = "$pIndex-$dIndex";
        }
      }
    }
    
    print_r($result);
    

    The first step of this solution is to build a mapping like this:

    // From this:
    [ 'Pickup', 'Delivery', 'Pickup', 'Delivery' ]
    // to this mapping:
    [ 0 => false, 1 => true, 2 => false, 3 => true ]
    

    Setting the ‘Delivery’ entries to true and not the ‘Pickup’ entries allows for array_filter to be used without supplying a closure as array_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.

    Login or Signup to reply.
  3. 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)..

    $stops = array('Pickup', 'Delivery', 'Pickup', 'Delivery', 'Pickup', 'Delivery', 'Pickup', 'Delivery');
    $pickUps = [];
    $deliveries = [];
    foreach($stops as $key => $stop)    {
        if ($stop == 'Pickup')  {
            $pickUps[] = $key;
        }
        else    {
            $deliveries[] = $key;
        }
    }
    $result = [];
    foreach ($pickUps as $pickUp)   {
        foreach ($deliveries as $delivery)  {
            if ($delivery < $pickUp)    {
                array_shift($deliveries);
            }
            else {
                $result[] = $pickUp . '-' . $delivery;
            }
        }
    }
    
    print_r($result);
    
    Login or Signup to reply.
  4. 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

    function group(array $data): array
    {
        $res = [];
        foreach ($data as $k => $v) {
            $existing = $res[$v] ?? [];
            $existing[] = $k;
            $res[$v] = $existing;
        }
    
        return $res;
    }
    

    if you call this function on your array you get something like this

    $stops = array('Pickup', 'Delivery', 'Pickup', 'Delivery');
    
    
    $grouped = group($stops)
    
    $result = [];
    
    $grouped = group($stops);
    
    foreach ($grouped['Pickup'] as $p) {
        foreach ($grouped['Delivery'] as $d){
            if($p < $d){
                $result[] = sprintf('%d-%d', $p, $d);
            }
        }
    }
    
    
    print_r($result);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search