0

Is there a fast way to compare two arrays, where the second array contains all the keys from the first, but also has additional keys?

For example, given the two arrays below, I would want the final two elements from the first array as they do not exist on the second:

$input = [
    [
        'firstName' => 'Paula',
        'lastName' => 'Fisher',
        'companyName' => 'Ankunding-Braun'
    ],
    [
        'firstName' => 'Elliot',
        'lastName' => 'Roob',
        'companyName' => 'Feeney PLC'
    ],
    [
        'firstName' => 'Jammie',
        'lastName' => 'Morar',
        'companyName' => 'Pollich PLC'
    ],
    [
        'firstName' => 'Tyrell',
        'lastName' => 'Mills',
        'companyName' => 'Oberbrunner, Kulas and Rice'
    ],
    [
        'firstName' => 'Fred',
        'lastName' => 'Johnson',
        'companyName' => 'Pollich PLC'
    ],
    [
        'firstName' => 'Tyrell',
        'lastName' => 'Bloggs',
        'companyName' => 'BBC East'
    ],
];

$output = [
    [
        "id" => 1,
        "firstName" => "Paula",
        "lastName" => "Fisher",
        "salutation" => "Prof.",
        "email" => "[email protected]",
        "phone" => "1-887-271-5742 x394",
        "mobileNumber" => "1-558-612-4089 x45355"
    ],
    [
        "id" => 2,
        "firstName" => "Elliot",
        "lastName" => "Roob",
        "salutation" => "Prof.",
        "email" => "[email protected]",
        "phone" => "+1-378-385-3633",
        "mobileNumber" => "1-815-769-2297",
    ],
    [
        "id" => 3,
        "firstName" => "Jammie",
        "lastName" => "Morar",
        "salutation" => "Mr.",
        "email"=> "[email protected]",
        "phone" => "(694) 767-1593 x5966",
        "mobileNumber" => "204-991-3292",
    ],
    [
        "id" => 4,
        "firstName" => "Tyrell",
        "lastName" => "Mills",
        "salutation"=> "Mrs.",
        "email" => "[email protected]",
        "phone" => "462-385-0569 x22876",
        "mobileNumber" => "532-369-9039"
    ]
];

As a little bit of context, I am trying to return all records that do not exist on the database (checking first name, last name and company name). If there's a faster way of doing this using the database that would be great, but I can't think of anything myself, so I've returned the records that are found, and now want to remove the found records from the searched array - therefore leaving me with the records that do not exist.

3
  • Are the keys (columns) you're looking to compare always the same? Do that in the query. What do the other fields have to do with it unless it's arbitrary which columns to compare? Commented Jun 11, 2017 at 15:03
  • Because I need the full contact model as I'll be doing something else with them. Basically, I want to enter a load of first names, last names, and company names, and then do something with the records that were found, and list the records that weren't found. I'm using Lumen and Doctrine2 Commented Jun 11, 2017 at 15:12
  • What do they have to do with the filtering? That's what you're asking. You ignore them, unless I'm missing something. You also don't have companyName as a field in the $output, so it seems like it can't be compared for that column on filter given what's provided. I'm writing an answer, bear with me. Commented Jun 11, 2017 at 15:14

1 Answer 1

1

To find the searches not found in the results, you can:

// I can't "query", this is for demonstration based on the question data.
// The assumption here is that this is a hydrated to array resultset from a
// Doctrine ORM query, but it should work for any array comparison of this 
// sort.
$filtered = array_filter($output, function($row) use($input) {
    return array_reduce($input, function($carry, $compare) use($row) {
        return $carry || (
            $compare['firstName'] == $row['firstName']
            && $compare['lastName'] == $row['lastName']
            // Uncomment this if you want, and have, companyName to compare.
            // && $compare['companyName'] == $row['companyName']
        );
    });
});

$notfound = array_filter($input, function($search) use($filtered) {
    // Note the negation here, this uses array_reduce() to tell us when
    // there's a "hit", we're looking for $search rows with no "hit".
    return !array_reduce($filtered, function($carry, $row) use($search) {
        return $carry || (
            $search['firstName'] == $row['firstName']
            && $search['lastName'] == $row['lastName']
            // Uncomment this if you want, and have, companyName to compare.
            // Note that the hydrated resultset needs to account for this.
            // && $compare['companyName'] == $row['companyName']
        );
    });
});

https://3v4l.org/Rck9V

Sign up to request clarification or add additional context in comments.

13 Comments

The company name is an important predicate as that's part of the search. For example, I'd want to check if Fred Johnson who works at the BBC exists of the database. So I check to see whether there is a record with the first name of Fred, last name of Johnson, then join the company and check the company name is BBC. If that particular record doesn't exist, I want to do something with it, so I need to know what I searched for that returned null/nothing. The company name isn't in the second array because it's hydrated by Doctrine as it's an entity relationship, not a column definition.
Then add it. You simply didn't take account of it in your $output array, so I didn't either. It's another check in the array_reduce() return condition. If you're using Doctrine ORM, you really should use DQL for that (which I'm not much of an expert on). To use arrays, you would need to hydrate it in such a way that your source to filter on has it somewhere.
I am using DQL, for the query, but there doesn't appear to be a way of saying 'create an array of the queries that returned nothing'. Thanks anyway
Create an array of the queries that return nothing? What are you talking about?
Like this? 3v4l.org/Rck9V (I'm still ignoring companyName because I'm lazy, but the results are the same due to the data provided.)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.