skip to Main Content

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;
});

3

Answers


  1. 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);
    
    Login or Signup to reply.
  2. 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.

    References:

    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

    Demo:

    https://onlinephp.io/c/c638f

    <?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);
    
    Login or Signup to reply.
  3. Until there is further clarification from the asker (and a better representing set of sample data), I am going assume:

    • The first array will not contain duplicated email values.
    • Related rows in the first array must be disqualified if duplicated (conflicting) values for a given email are found in the second array..

    1. Populate a result array from the first array and assign tempoary, first-level associative keys so that the array can be used as a lookup while processing.
    2. Populate another lookup array containing unique email values and their respective counts in the second array.
    3. Iterate over the second array and either unset the related row in the result array if it has been disqualified or append the row’s data to the corresponding result row.

    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));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search