2
\$\begingroup\$

I made this function to calculate the difference between 2 arrays and return it in a third array. I'm using this function as a part of stocktaking functionality I apply in my accounting system.

function stockdiff ($array2,$array1){
    //inputs :
    //$array2 is the stocktaking result in an array that has the format "$product_id=>$quantity"
    //$array1 is the stock according to the data in the system in an array that has the format "$product_id=>$quantity"
    foreach($array2 as $key=> $value){
        if(isset($array1[$key])){$result[$key] = $array2[$key] - $array1[$key];}
        else{$result[$key] = $array2[$key];}
    }
    $array1diff = array_diff_key($array1,$array2);

    foreach($array1diff as $key=> $value){
        $result[$key] = -1 * $array1[$key];
    }
return $result; 
}

Case example

When the user do the stocktaking, its result are going to be saved in $array2 in the format $product_id=>$quantity.

This is a stocktaking result example:

$array2[2] = 500;
$array2[3] = 7;
$array2[1] = 302;
$array2[105] = 7000;
$array2[7] = 304;
$array2[8] = 20;
$array2[9] = 20;
$array2[11] = 20;
$array2[73] = 32;
$array2[21] = 35;

I then compare it against the stock quantities according to the data in the system $array1 which might be something like this:

$array1[1] = 30;
$array1[2] = 60;
$array1[3] = 202;
$array1[4] = 200;
$array1[7] = 0;
$array1[8] = 0;
$array1[9] = 0;
$array1[11] = 52;
$array1[21] = 70;
$array1[99] = 21;

I made this function to do that job. I need it to tell me the changes that happened for every product between the data in the system and the actual data come from the stocktaking, for example, let's take product_id 2.

In the system it was $array1[2] = 60; 60 units from the product 2, and the stocktaking told us no, we found 500 units from the product 2 $array2[2] = 500;. So, the function must return [2]=>440.

The function also needs to consider about these points:

  1. If there is a product in the stocktaking array ($array2) that has no matching product in the stock-in-system array($array1), then the function will assume it's whole quantity has been increased and return it's quantity from $array2.
  2. If there is a product in the stock-in-system array ($array1) that has no matching product in the stocktaking array ($array2), then the function will assume it's whole quantity has been decreased and return -1 * quantity.

What do you think about the function?

\$\endgroup\$
2
\$\begingroup\$

Regarding the way to achieve this task I couldn't find any better strategy than yours.

On the other hand your code may be made cleaner and more readable by:

  • first merely following best-practices (don't put several statements in the same line,
  • using more significant names (such as $initial and $final instead of $array1 and $array2, $final_value and $initial_value rather than only $value)
  • avoiding to declare useless variables (like $array1diff, used only once)
  • using the more direct expressions when possible (e.g. $array2[key] is already available as $value, while -1 * $array1[$key] is merely -$array1[$key])
  • using the ternary operator where it can simplify the code (see also the note below)

It results in a reduced code:

function stock_diff($initial, $final) {
    foreach ($final as $key => $final_value) {
        $result[$key] = $final_value - @$initial[$key] ?: 0;
    }
    foreach(array_diff_key($initial, $final) as $key => $initial_value) {
        $result[$key] = -$initial_value;
    }
    return $result; 
}

NOTE: regarding the use of @ in the ternary operator, I know that many people automatically banish it as globally evil.
I don't agree with this too general point of view: sure it must be avoided in many situations where it might lead to some issues, but it's pretty legal when used carefully and with discernment.

In the case above, the only "error" it may hide is the one we know it may happen (undefined index), so it keeps sure.
And so it helps making the code more readable and reduced:
(isset($initial[$key]) ? $initial[$key] : 0)
is replaced by
@$initial[$key] ?: 0

The only con is about performance: using @ works slightly slower than isset(). So avoid it when you know that a piece of code will be executed a significantly huge number of times.

\$\endgroup\$
  • \$\begingroup\$ Thank you so much for your advises , I'm going to take every point into serious consideration. I also want to ask you about something regarding this line foreach(array_diff_key($initial, $final) Isn't that going to call array_diff_key() function for every iteration in the loop, or the loop is going to call it only at the first iteration? \$\endgroup\$ – Accountant م Nov 17 '16 at 19:04
  • 1
    \$\begingroup\$ @Accountantم Glad to help. Regarding your question: no, the array_diff_key() function is called only once, at the foreach() init time. In the other hand, your remark made me think to add a precision about the performance with @: see my edited answer. \$\endgroup\$ – cFreed Nov 17 '16 at 20:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.