Reputation: 41
How do I get the elements from the two arrays below where the value of email
key only exists once from either of the list, and then merge their keys?
Say, I got two arrays:
$arr1 = [
['email' => '[email protected]', 'name' => 'John Doe'],
['email' => '[email protected]', 'name' => 'Johnny Sins'],
['email' => '[email protected]', 'name' => 'Jose Alvarado']
];
$arr2 = [
['email' => '[email protected]', 'country' => 'Japan'],
['email' => '[email protected]', 'country' => 'China'],
['email' => '[email protected]', 'country' => 'Korea'],
];
The final result should be:
[
['email' => '[email protected]', 'name' => 'John Doe'],
['email' => '[email protected]', 'name' => 'Johnny Sins', 'country' => 'Korea'],
];
I tried the following, but I'm stuck on merging the key:
$merged = array_merge($arr1, $arr2);
$result = array_filter($merged, function($value) use($merged) {
return array_count_values(array_column($merged, 'email'))[$value['email']] < 3;
});
Upvotes: 2
Views: 77
Reputation: 47874
Until there is further clarification from the asker (and a better representing set of sample data), I am going assume:
Code: (Demo)
$result = array_column($arr1, null, 'email');
$counts = array_count_values(array_column($arr2, 'email'));
foreach ($arr2 as $row) {
if ($counts[$row['email']] > 1) {
unset($result[$row['email']]); // disqualify row because duplicated in arr2
} else {
$result[$row['email']] += $row; // append arr2 data to result data
}
}
var_export(array_values($result));
If either array may have disqualifying rows, then the following approach will sanitize and merge the two arrays on their shared email value with a low time complexity.
Code: (Demo)
function sanitizeAndKey($array) {
$found = [];
$clean = [];
foreach ($array as $row) {
if (isset($found[$row['email']])) {
unset($clean[$row['email']]);
} else {
$found[$row['email']] = $row;
$clean[$row['email']] = $row;
}
}
return $clean;
}
$result = sanitizeAndKey($arr1);
foreach (sanitizeAndKey($arr2) as $key => $row) {
$result[$key] = ($result[$key] ?? []) + $row; // append arr2 data to arr1 data
}
var_export(array_values($result));
Upvotes: 1
Reputation: 57121
After misunderstanding your question, I've made another attempt. The complication is that you need to check for duplicates in each array and not the joint results.
This first checks for duplicates and creates a list of the ones that should be excluded...
function summarize($array) {
$arrCount = array_count_values(array_column($array, 'email'));
return array_filter($arrCount, function ($value) {
return $value > 1;
}); }
$result1 = summarize($arr1);
$result2 = summarize($arr2);
$jointDuplicates = array_merge($result1, $result2);
What it then does is it builds a list of the non-duplicate values, merging in new values as it encounters existing values, creating a new element where it doesn't...
$jointList = array_merge($arr1, $arr2);
$result = [];
foreach ($jointList as $value) {
$email = $value['email'];
if (isset($jointDuplicates[$email])) {
continue;
}
if (isset($result[$email]) === false) {
$result[$email] = [];
}
$result[$email] = array_merge($result[$email], $value);
}
print_r($result);
Upvotes: 1
Reputation: 8163
My approach used in this demo was first determining on both arrays if there are any duplicated email. Such check will produce an array containing all the emails that shouldn't be taken into account as the marge of the duplicated emails coming from both arrays.
Then you can use such array when filtering the first and second array so that it will be returned only the entries NOT having any email contained in the above cited array of not allowed emails.
In the end it's a matter of returning the filtered arrays merged.
https://www.php.net/manual/en/function.array-column
https://www.php.net/manual/en/function.array-count-values
https://www.php.net/manual/en/function.array-filter
https://www.php.net/manual/en/function.array-keys
https://www.php.net/manual/en/function.in-array
https://www.php.net/manual/en/function.array-merge
<?php
$arr1 = [
['email' => '[email protected]', 'name' => 'John Doe',],
['email' => '[email protected]', 'name' => 'Johnny Sins',],
['email' => '[email protected]', 'name' => 'Jose Alvarado',],
];
$arr2 = [
['email' => '[email protected]', 'country' => 'Japan',],
['email' => '[email protected]', 'country' => 'China',],
['email' => '[email protected]', 'country' => 'Korea',],
];
//returns an array with emails being duplicated in $arr
function getDuplicatedEmails($arr){
//gets list of emails found from entries in $arr
$emails = array_column($arr, 'email');
//gets how many time each email is occurring in $emails
$emailsWithCounts = array_count_values($emails);
//filteres the above array returning only values with repetitions
$duplicates = array_filter($emailsWithCounts, function($value){ return $value > 1; });
//returns the emails found
return array_keys($duplicates);
}
//returns an array like $arr minus the entries having [email] included in $notAllowed
function filterByEmailNotAllowed($arr, $notAllowed = []){
//filter the list of emails
$filtered = array_filter($arr, function ($item) use ($notAllowed) {
//return this item if the email is not included in $notAllowed
return !in_array($item['email'], $notAllowed);
});
return $filtered;
}
//merge the arrays arr1 and arr2 excluding entries having emails duplicated in any of the two input arrays
function merge($arr1, $arr2){
$emailNotAllowed1 = getDuplicatedEmails($arr1);
$emailNotAllowed2 = getDuplicatedEmails($arr2);
$emailNotAllowed = array_merge($emailNotAllowed1, $emailNotAllowed2);
$filter1 = filterByEmailNotAllowed($arr1, $emailNotAllowed);
$filter2 = filterByEmailNotAllowed($arr2, $emailNotAllowed);
$filter = array_merge($filter1, $filter2);
return $filter;
}
$result = merge($arr1, $arr2);
var_dump($result);
Upvotes: 2