Hello All,

I have 2 data sources. Data source A needs information to be updated on a regular basis. It receives it's updated information from data source B. There are many thousands of records involved here so I am trying to limit the amount of MySql queries as much as possible. I've managed to get both of these data sources into separate multi-dimensional arrays.

I need to write a function that will compare data source A with data source B and, in a new array, have the keys that need to be changed with only the values that are different.

Here is a visual:

Multi-dimensional array from datasource A

Array
(
    ["product_a"] => 
        Array (
                       ["description"]=>"Product A is Large"
                       ["color"]=>"Red"
                       ["quantity"]=>5
                  )
    ["product_b"] => 
        Array (
                       ["description"]=>"Product B is Small"
                       ["color"]=>"Blue"
                       ["quantity"]=>20
                  )
    ["product_c"] => 
        Array (
                       ["description"]=>"Product C has batteries included"
                       ["color"]=>"Green"
                       ["quantity"]=>7
                  )
)

Multi-dimensional array from datasource B

Array
(
    ["product_a"] => 
        Array (
                       ["description"]=>"Product A is Large"
                       ["color"]=>"Yellow"
                       ["quantity"]=>8
                  )
    ["product_b"] => 
        Array (
                       ["description"]=>"Product B is Small"
                       ["color"]=>"Blue"
                       ["quantity"]=>20
                  )
    ["product_c"] => 
        Array (
                       ["description"]=>"Product C is made of Wax"
                       ["color"]=>"Green"
                       ["quantity"]=>7
                  )
)

Here is what the new array should look like:

Array
(
    ["product_a"] => 
        Array (
                       ["color"]=>"Yellow"
                       ["quantity"]=>8
                  )
    ["product_c"] => 
        Array (
                       ["description"]=>"Product C is made of Wax"
                  )
)

I know the function probably uses some form of the php built in array_diff function and I am sure there is recursion involved but I can't, for the life of me, figure out how to do this.

I am a beginner programmer so any help would be much appreciated.

Dani AI

Generated

A small caution: array_diff() compares values only and casts them to strings, so it can miss changes (e.g., 8 vs "8") and it drops keys. For associative records you usually want a key-aware, strict comparison and to return only the fields from B that actually changed. The helper below does that, supports nested arrays, and lets you optionally limit which fields to consider (e.g., RRP/Cost).

function diffChanged(array $a, array $b, array $whitelist = null): array
{
    $out = [];
    foreach ($b as $key => $bVal) {
        if ($whitelist && !in_array($key, $whitelist, true)) {
            continue; // ignore fields not in the whitelist
        }
        if (!array_key_exists($key, $a)) {
            $out[$key] = $bVal;            // new field in B
            continue;
        }
        $aVal = $a[$key];
        if (is_array($aVal) && is_array($bVal)) {
            $child = diffChanged($aVal, $bVal, $whitelist);
            if ($child) $out[$key] = $child;
        } elseif ($aVal !== $bVal) {       // strict comparison
            $out[$key] = $bVal;
        }
    }
    return $out;
}

// Build the per-product changes from B relative to A.
$updates = [];
foreach ($datasourceB as $productId => $rowB) {
    if (!isset($datasourceA[$productId])) continue; // or treat as an insert
    $diff = diffChanged($datasourceA[$productId], $rowB, ['description','color','quantity','rrp','cost']);
    if ($diff) $updates[$productId] = $diff;
}

See the docs for the behavior of array_diff and why you might prefer key-aware comparisons like array_diff_assoc. For strict comparison semantics (===), see PHP comparison operators.

I think I figured it out and it's remarkably simple (almost too simple).

I have two ways of doing it though:

This way seems to perform about 10% faster according to my benchmarks.

foreach ($products as $k1 => $v1) {
	if (array_diff($excel_items[$k1], $products[$k1])){
		$update_items[$k1] = array_diff($excel_items[$k1], $products[$k1]);
	}
}

This way works as well but a tad slower.

foreach ($products as $k1 => $v1) {
	$update_items[$k1] = array_diff($excel_items[$k1], $products[$k1]);
	if(empty($update_items[$k1])){
		unset($update_items[$k1]);
	}
}

In both, I am just looping through the keys of the data source A and using array_diff, checking for differences in the other data souce B. If there is a difference I add it to a new array.

The first function works well but I don't like that I have to run the array_diff funtion twice on each iteration (there has to be a cleaner way of doing that).

With the second function I do the array_diff (it returns an empty array if there is nothing different which I thought to be unusual) and then just unset the key if it's empty. I liked this way better but the performance testing is telling me otherwise.

Any ways I can improve this?

Is there anyway to do this better?

foreach ($products as $k1 => $v1) {
	$update_items[$k1] = array_diff($excel_items[$k1], $products[$k1]);
	if(empty($update_items[$k1])){
		unset($update_items[$k1]);
	}
}

This is working a charm, how can I add support for RRP and Cost key's on both Array A and Array B (i.e. if a product RRP or Cost changes the item is added to $update_items).

Hello BHance
Here's how you can improve the function.

foreach ($products as $k1 => $v1)
{

        if ( $u = array_diff($excel_items[$k1], $products[$k1]) )
        {

                $update_items[$k1] = $u;

        }

}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.