dynamic count of shorthand comparisons in PHP

Viewed 36

I have the following code:

<?php

$a =   [
            [
                'id' => 20,
                'created_at' => '2020-11-22',
                'updated_at' => '2020-11-22 11:16:22',
                'name' => 'AA',
            ],
            [
                'id' => 19,
                'created_at' => '2020-11-27 11:16:22',
                'updated_at' => null,
                'name' => 'BB',
            ]
    ];

$b =   [
            [
                'id' => 20,
                'created_at' => '2020-11-22 11:16:11',
                'updated_at' => '2020-11-22 11:16:22',
                'name' => 'AA',
            ],
            [
                'id' => 19,
                'created_at' => '2020-11-27 11:16:22',
                'updated_at' => null,
                'name' => 'BB',
            ]
    ];


function array_diff_by_keys(array $a, array $b)
{
    $compare = function ($x, $y) {
        return $x['id'] <=> $y['id'] ?: 
               $x['created_at'] <=> $y['created_at'] ?: 
               $x['updated_at'] <=> $y['updated_at']; // how to build dynamic?
    };

    return array_udiff($a, $b, $compare);
}

array_diff_by_keys($a, $b); // what i've got
array_diff_by_keys($a, $b, ['id', 'created_at', ...]); // what i want

I would like in the $compare function create dynamic condition based on arguments passed to the function. Currently I have predefined keys like (id, created_at etc.), and I would like to be able to decide what arguments are to be included in the Elvis operator

1 Answers

You can iterate over the keys you want to compare on, taking the diff of the corresponding values in $x and $y, and returning that diff if it's non-zero; otherwise moving on to the next key. This function allows you to not specify the desired keys to compare on, if you don't they default to all the keys in each element:

function array_diff_by_keys(array $a, array $b, array $keys = null)
{
    if (empty($keys)) $keys = array_keys(reset($a));
    
    $compare = function ($x, $y) use ($keys) {
        foreach ($keys as $key) {
            $diff = $x[$key] <=> $y[$key];
            if ($diff) return $diff;
        }
        return $diff;
    };

    return array_udiff($a, $b, $compare);
}

Output (for your sample data):

Array
(
    [0] => Array
        (
            [id] => 20
            [created_at] => 2020-11-22
            [updated_at] => 2020-11-22 11:16:22
            [name] => AA
        )
)

Demo on 3v4l.org

Related