bbe
bbe

Reputation: 344

Merging multidimensional arrays by matching key-value pairs of subarrays?

The task is to merge ("inexpensively") two arrays, which have matching key-value pairs of subarrays. E.g.:

Array 1:

Array
(
[0] => Array
    (
        [count] => 1
        [da_table] => article
        [da_class] => classes\elements\tables\Article
        [da_page_class] => Page_Article
    )

[1] => Array
    (
        [count] => 2
        [da_table] => client_contract_specification_price
        [da_class] => classes\elements\tables\ClientContractSpecificationPrice
        [da_page_class] => Page_ClientContractSpecification
    )

[2] => Array
    (
        [count] => 2
        [da_table] => supplier
        [da_class] => classes\elements\tables\Supplier
        [da_page_class] => Page_Supplier
    )

)

Array 2:

Array
(
[0] => Array
    (
        [name] => Articles
        [name_short] => 
        [da_page_class] => Page_Article
    )

[1] => Array
    (
        [name] => Client contract specifications
        [name_short] => cc_specifications
        [da_page_class] => Page_ClientContractSpecification
    )

[2] => Array
    (
        [name] => Suppliers
        [name_short] => 
        [da_page_class] => Page_Supplier
    )

)

How to merge the two above arrays by a matching [da_page_class] => ... pairs, so the resulting array will contain both key-values of the first and the second array, i.e.:

...
[0] => Array
    (
        [count] => 1
        [da_table] => article
        [da_class] => classes\elements\tables\Article
        [da_page_class] => Page_Article
        [name] => Articles
        [name_short] => 
    )
...

Additional requirements: Subarrays may come in random order. Also, there can be "orphans", which contain values of ['da_page_class'], but have no match in another array. These should be ignored.

Upvotes: 0

Views: 1067

Answers (2)

masterfloda
masterfloda

Reputation: 3038

O(m*n) solution:

$result = array();

foreach ($array1 as $value1) {
    // do not handle elements without pageclass
    if (!array_key_exists('da_page_class', $value1) || !$value1['da_page_class']) {
        continue;
    }

    foreach ($array2 as $value2) {
        if (!array_key_exists('da_page_class', $value2) || !$value2['da_page_class']) {
            continue;
        }
        if ($value1['da_page_class'] == $value2['da_page_class']) {
            array_push($result, $value1 + $value2)
            break;
        }
    }
}

print_r($result);

O(m+n) solution:

$result = array();

foreach ($array1 as $value) {
    // do not handle elements without pageclass
    if (!array_key_exists('da_page_class', $value) || !$value['da_page_class']) {
        continue;
    }
    $result[$value['da_page_class']] = $value;
}
foreach ($array2 as $value) {
    if (
        // do not handle elements without pageclass         
        !array_key_exists('da_page_class', $value) || !$value['da_page_class'] ||
        // do not handle elements that do not exist in array 1
        !array_key_exists($value['da_page_class'], $result)
        ) {
        continue;
    }
    // merge values of this pageclass
    $result[$value['da_page_class']] = array_merge($result[$value['da_page_class']], $value);
}

print_r($result);

EDIT: added O(m+n) solution

Upvotes: 2

arkascha
arkascha

Reputation: 42935

Well, you simply iterate over the array elements and combine them:

<?php
$data1 = [
    [
        'count' => 1,
        'da_table' => 'article',
        'da_class' => 'classes\elements\tables\Article',
        'da_page_class' => 'Page_Article'
    ],
    [
        'count' => 2,
        'da_table' => 'client_contract_specification_price',
        'da_class' => 'classes\elements\tables\ClientContractSpecificationPrice',
        'da_page_class' => 'Page_ClientContractSpecification'
    ],
    [
        'count' => 2,
        'da_table' => 'supplier',
        'da_class' => 'classes\elements\tables\Supplier',
        'da_page_class' => 'Page_Supplier'
    ]
];

$data2 = [
    [
        'name' => 'Articles',
        'name_short' => null,
        'da_page_class' => 'Page_Article'
    ],
    [
        'name' => 'Client contract specifications',
        'name_short' => 'cc_specifications',
        'da_page_class' => 'Page_ClientContractSpecification'
    ],
    [
        'name' => 'Suppliers',
        'name_short' => null,
        'da_page_class' => 'Page_Supplier'
    ]
];

$output = [];
for ($i=0; $i<count($data1); $i++) {
    $output[$i] = array_merge($data1[$i], $data2[$i]);
}
print_r($output);

The output obviously is:

Array
(
    [0] => Array
        (
            [count] => 1
            [da_table] => article
            [da_class] => classes\elements\tables\Article
            [da_page_class] => Page_Article
            [name] => Articles
            [name_short] =>
        )

    [1] => Array
        (
            [count] => 2
            [da_table] => client_contract_specification_price
            [da_class] => classes\elements\tables\ClientContractSpecificationPrice
            [da_page_class] => Page_ClientContractSpecification
            [name] => Client contract specifications
            [name_short] => cc_specifications
        )

    [2] => Array
        (
            [count] => 2
            [da_table] => supplier
            [da_class] => classes\elements\tables\Supplier
            [da_page_class] => Page_Supplier
            [name] => Suppliers
            [name_short] =>
        )

)

Alternatively you could also merge the contents of the elements of the second array into the corresponding elements of the first array. That reduces the memory footprint for large data sets.


Considering the additional requirement you specified in your comment I changed the merge strategy to allow for arbitrary orders of the two sets:

<?php
$data1 = [
    [
        'count' => 1,
        'da_table' => 'article',
        'da_class' => 'classes\elements\tables\Article',
        'da_page_class' => 'Page_Article'
    ],
    [
        'count' => 2,
        'da_table' => 'client_contract_specification_price',
        'da_class' => 'classes\elements\tables\ClientContractSpecificationPrice',
        'da_page_class' => 'Page_ClientContractSpecification'
    ],
    [
        'count' => 2,
        'da_table' => 'supplier',
        'da_class' => 'classes\elements\tables\Supplier',
        'da_page_class' => 'Page_Supplier'
    ]
];

$data2 = [
    [
        'name' => 'Articles',
        'name_short' => null,
        'da_page_class' => 'Page_Article'
    ],
    [
        'name' => 'Client contract specifications',
        'name_short' => 'cc_specifications',
        'da_page_class' => 'Page_ClientContractSpecification'
    ],
    [
        'name' => 'Suppliers',
        'name_short' => null,
        'da_page_class' => 'Page_Supplier'
    ]
];

$output = [];
array_walk($data1, function($entry, $key) use (&$output, $data2) {
    $output[$key] = $entry;
    foreach($data2 as $cand) {
        if ($entry['da_page_class'] == $cand['da_page_class']) {
            $output[$key] = array_merge($output[$key], $cand);
        }
    }
});
print_r($output);

The resulting output obviously is identical to above.

Upvotes: 3

Related Questions