phparraysmultidimensional-arraysummerging-data

Merge and sum data from two associative multidimensional arrays


I have this function (I got as the answer to this question) that merges an array like so:

#Functions

function readArray( $arr, $k, $default = 0 ) {
    return isset( $arr[$k] ) ? $arr[$k] : $default ;
}

function merge( $arr1, $arr2 ) {
    $result = array() ;
    foreach( $arr1 as $k => $v ) {
        if( is_numeric( $v ) ) {
            $result[$k] = (int)$v + (int) readArray( $arr2, $k ) ;
        } else {
            $result[$k] = merge( $v, readArray($arr2, $k, array()) ) ;
        }
    }
    return $result ;
}

#Usage

$basketA = array( "fruit" => array(), "drink" => array() ) ;
$basketA['fruit']['apple'] = 1;
$basketA['fruit']['orange'] = 2;
$basketA['fruit']['banana'] = 3;
$basketA['drink']['soda'] = 4;
$basketA['drink']['milk'] = 5;

$basketB = array( "fruit" => array(), "drink" => array() ) ;
$basketB['fruit']['apple'] = 2;
$basketB['fruit']['orange'] = 2;
$basketB['fruit']['banana'] = 2;
$basketB['drink']['soda'] = 2;
$basketB['drink']['milk'] = 2;

$basketC = merge( $basketA, $basketB ) ;
print_r( $basketC ) ;

#Output

Array
(
    [fruit] => Array
        (
            [apple] => 3
            [orange] => 4
            [banana] => 5
        )

    [drink] => Array
        (
            [soda] => 6
            [milk] => 7
        )

)

OK this works with 1 flaw I cannot figure out how to fix: if $arr1 is missing something that $arr2 has, it should just use the value from $arr2 but instead omits it all together:

#Example

$basketA = array( "fruit" => array(), "drink" => array() ) ;
$basketA['fruit']['apple'] = 1;
$basketA['fruit']['orange'] = 2;
$basketA['fruit']['banana'] = 3;
$basketA['drink']['milk'] = 5;

$basketB = array( "fruit" => array(), "drink" => array() ) ;
$basketB['fruit']['apple'] = 2;
$basketB['fruit']['orange'] = 2;
$basketB['fruit']['banana'] = 2;
$basketB['drink']['soda'] = 2;
$basketB['drink']['milk'] = 2;

$basketC = merge( $basketA, $basketB ) ;
print_r( $basketC ) ;

#Output

Array
(
    [fruit] => Array
        (
            [apple] => 3
            [orange] => 4
            [banana] => 5
        )

    [drink] => Array
        (
            [milk] => 7
        )

)

Notice how [soda] is not in the new array because the first array did not have it.

How can I fix this?


Solution

  • Quick fix, change the merge() function to look like this:

    function merge( $arr1, $arr2 ) {
        $result = array() ;
        foreach( $arr1 as $k => $v ) {
            if( is_numeric( $v ) ) {
                $result[$k] = (int)$v + (int) readArray( $arr2, $k ) ;
            } else {
                $result[$k] = merge( $v, readArray($arr2, $k, array()) ) ;
            }
        }
        foreach( $arr2 as $k => $v ) {
            if( is_numeric( $v ) ) {
                $result[$k] = (int)$v + (int) readArray( $arr1, $k ) ;
            } else {
                $result[$k] = merge( $v, readArray($arr1, $k, array()) ) ;
            }
        }
        return $result ;
    }
    

    Output:

    Array
    (
        [fruit] => Array
            (
                [apple] => 3
                [orange] => 4
                [banana] => 5
            )
    
        [drink] => Array
            (
                [soda] => 2
                [milk] => 7
            )
    )
    

    It's also worth noticing that array_merge_recursive() alone does almost the same:

    $basketC = array_merge_recursive($basketA, $basketB);
    

    Output:

    Array
    (
        [fruit] => Array
            (
                [apple] => Array
                    (
                        [0] => 1
                        [1] => 2
                    )
    
                [orange] => Array
                    (
                        [0] => 2
                        [1] => 2
                    )
    
                [banana] => Array
                    (
                        [0] => 3
                        [1] => 2
                    )
    
            )
    
        [drink] => Array
            (
                [milk] => Array
                    (
                        [0] => 5
                        [1] => 2
                    )
    
                [soda] => 2
            )
    )
    

    So if you wanted to know how many oranges are in $basketC, you would just have to do:

    array_sum((array)$basketC['fruit']['orange']); // 4
    

    This way you don't need to use any hackish, slow and unproved custom function.