Shijin TR
Shijin TR

Reputation: 7788

Merge rows of two 2d arrays based on the shared column value and apply default values where expected column is missing

I have two arrays which must be merged together on the dt_date column and I need to declare subscribers and unsubscribers in all rows in the result.

$array1 = [
    ['subscribers' => 2, 'dt_date' => '2014-02-27'],
    ['subscribers' => 2, 'dt_date' => '2014-02-25'],
    ['subscribers' => 1, 'dt_date' => '2014-02-07']
];
$array2 = [
    ['unsubscribers' => 1, 'dt_date' => '2014-02-27'],
    ['unsubscribers' => 1, 'dt_date' => '2014-02-01']
];

I need to create an array like as follows from the two arrays.

[
    ['subscribers' => 2, 'unsubscribers' => 1, 'dt_date' => '2014-02-27'],
    ['subscribers' => 2, 'unsubscribers' => 0, 'dt_date' => '2014-02-25'],
    ['subscribers' => 1, 'unsubscribers' => 0, 'dt_date' => '2014-02-07'],
    ['subscribers' => 0, 'unsubscribers' => 1, 'dt_date' => '2014-02-01']
]

I tried

$result = array_merge_recursive($aray1, $array2);

AND

$result = array_merge(
    $array1, 
    array_udiff(
        $array2,
        $array1, 
        function($array1,$array2){
            return strcmp($array1['dt_date'],$array2['dt_date']);
        }
    )
);  

But the result was not as expected

Upvotes: 0

Views: 130

Answers (3)

mickmackusa
mickmackusa

Reputation: 48073

  1. Merge the two arrays and loop over them.
  2. Assign temporary first level keys as you push data into the result array so that you can swiftly identify related subsequently encountered rows.
  3. If the date hasn't been encountered before, merge the row's data with the default row so that all expected elements are represented.
  4. If a given date is encountered after the first time, merge the stored row with the new row so that the new row's data overwrites the default value.
  5. When finished iterating, re-index the result array with array_values() (this removes the temporary first level keys).

Code: (Demo)

$result = [];
$default = array_fill_keys(['subscribers', 'unsubscribers'], 0);
foreach (array_merge($array1, $array2) as $row) {
    $result[$row['dt_date']] = array_merge($result[$row['dt_date']] ?? $default, $row);
}
var_export(array_values($result));

The functional-style equivalent: (Demo)

$default = array_fill_keys(['subscribers', 'unsubscribers'], 0);
var_export(
    array_values(
        array_reduce(
            array_merge($array1, $array2),
            function($result, $row) use($default) {
                $result[$row['dt_date']] = array_merge($result[$row['dt_date']] ?? $default, $row);
                return $result;
            }
        )        
    )
);

Upvotes: 0

Shijin TR
Shijin TR

Reputation: 7788

I find the answer

 $result= array_replace_recursive($array1,$array2);
 foreach ($result as $key => $value) {
    if(!isset($value['unsubscribers']))
        $result[$key]['unsubscribers']=0;
    if(!isset($value['subscribers']))
        $result[$key]['subscribers']=0;
 }

Thank you

Upvotes: 0

Alma Do
Alma Do

Reputation: 37365

Actually, your question can be resolved with some foreach stuff, but I've noticed that you're trying to emulate some kind of SQL JOIN behavior (with ON column as dt_date). That's why I've spent some more time and created more common solution.

First, we'll need to re-index our arrays so we could work with them faster:

function reindexArray($array, $column)
{
   return array_combine(
                array_map(function($x) use ($column)
                {
                   return $x[$column];
                }, $array),
                $array
   );
}

Function above could be easily replaced with array_column() call, but I assume you have PHP 5.3, thus you'll need "manual" re-indexing.

Next, join logic:

function arrayJoin($array1, $array2, $column, $default=null)
{
   //in PHP 5.5 it's just array_column() call:
   $array1 = reindexArray($array1, $column);
   $array2 = reindexArray($array2, $column);
   $blank1 = array_combine(
                array_keys(current($array1)), 
                array_fill(1, count(current($array1)), $default)
   );
   $blank2 = array_combine(
                array_keys(current($array2)), 
                array_fill(1, count(current($array2)), $default)
   );
   unset($blank1[$column], $blank2[$column]);

   return array_merge(
             array_map(function($x) use ($array1, $blank2)
             {
                return array_merge($array1[$x], $blank2);
             }, array_keys(array_diff_key($array1, $array2))),
             array_map(function($x) use ($array1, $array2)
             {
                return array_merge($array1[$x], $array2[$x]);
             }, array_keys(array_intersect_key($array1, $array2))),
             array_map(function($x) use ($array2, $blank1)
             {
                return array_merge($array2[$x], $blank1);
             }, array_keys(array_diff_key($array2, $array1)))
   );
}

-as you can see, you'll also need to decide what to do with non-set keys for each joined array. $default will fill that values for you. Therefore, your question would be resolved via:

$result = arrayJoin($array1, $array2, 'dt_date', 0);

-check this demo.

However, if your original array is derived from some SQL database, much better thing would be to place JOIN logic there - because intention of DB is to store data & select it properly.

Upvotes: 1

Related Questions