Home > OS >  Filter and merge data between two 2d arrays - result must not show rows that occur more than once in
Filter and merge data between two 2d arrays - result must not show rows that occur more than once in

Time:01-24

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

CodePudding user response:

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

CodePudding user response:

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

CodePudding user response:

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

CodePudding user response:

Simple loop concept:

$final = [];

foreach($arr1 as $key=>$ar){
    
    $data = array_count_values(array_column($arr2,'email'));
    
    if(!isset($data[$ar['email']])){
        $final[] = $ar;
    }
    
    if(isset($data[$ar['email']]) && $data[$ar['email']] < 2){
        $ar['country'] = $arr2[$key]['country'];
        $final[] = $ar;
    }
}

print_r($final);

Output: https://3v4l.org/s7dTO

  • Related