1

I have this multidimensional array that I need to find how many items for any SKU are sold.

How I can do?

    [Orders] => Array(
        [0] => Array(
            [transaction_id] => xxxx
            [date] => 1616277653
            [currency] => EUR
            [items] => Array(
                    [0] => Array(
                            [title] => Title
                            [quantity] => 200
                            [price] => 2.1
                            [sku] => 1T.810BK
                        )
                    [1] => Array(
                            [title] => Title
                            [quantity] => 250
                            [price] => 1.78
                            [sku] => 1090.005BK
                        )
              )
        )
        [1] => Array(
                [transaction_id] => 78079
                [date] => 1616597838
                [currency] => EUR
                [items] => Array(
                        [0] => Array
                            (
                                [title] => title
                                [quantity] => 3
                                [price] => 118.3
                                [sku] => 1.DIYBOX.ULT.638RE
                            )
                )
        )
)

For the moment I was using a function with array_walk_recursive inside, to extract all the values of a Key SKU. But in this way I cannot associate it with the value of the quantity.

So this can't be the solution, unless I can't pass 2 Key variables inside the function. It can be done?

This is the function that I use at this moment:

        function array_value_recursive($key, array $arr){
                $val = array();
                array_walk_recursive($arr, function($v, $k) use($key, &$val){
                    if($k == $key) array_push($val, $v);
                });
                return count($val) > 1 ? $val : array_pop($val);
        }

        array_value_recursive('sku', $GetPendingOrders_obj);

I'm not sure if passing two keys is the ideal solution. How do you think I can solve the problem of finding the quantity of the SKU sold?

Marco Romano
  • 456
  • 6
  • 21
  • 1
    Please express your exact desired output in your question. Please ALWAYS present your sample array data as `var_export()`'s output. Your sample data should probably include at least one duplicated SKU, so that incorrect answers are immediately identifiable. – mickmackusa Mar 29 '21 at 08:27
  • Your title doesn't have great searchability. Perhaps something like "Group and sum data in fourth level of multidimensional array". – mickmackusa Mar 29 '21 at 08:32
  • Hi @mickmackusa, I'm not sure which is the best way to export the data to find the quantity of the SKU sold. Could be another multidimensional array, but not really sure. Do you have some Idea? – Marco Romano Mar 29 '21 at 08:33
  • Thank you @mickmackusa I will change the tile, and also I will use this keyword for my google search – Marco Romano Mar 29 '21 at 08:34
  • I assume you would want a flat associative array as output so that the data structure is as lean as it can be. We have several "goup and sum" pages for you to reference, you will just need to implement enough loops to get down to the desired level. – mickmackusa Mar 29 '21 at 08:34
  • yes @mickmackusa, after I will use a foreach with a switch statement for display the data in a tab – Marco Romano Mar 29 '21 at 08:36
  • @MarcoRomano How should the output look like? – nice_dev Mar 29 '21 at 08:55
  • Hi @nice_dev, I will use a foreach with a switch statement for display the data in a tab. Link: This SKU = 3 pieces – Marco Romano Mar 29 '21 at 08:57
  • @MarcoRomano Not clear. Do you want count of quantity per SKU or total? – nice_dev Mar 29 '21 at 09:01
  • @nice_dev I need count of quantity per SKU. SKU1 = 3 SKU2 = 2 – Marco Romano Mar 29 '21 at 09:02
  • @MarcoRomano So in this context it is `1T.810BK => 200`, `1.DIYBOX.ULT.638RE => 3`, `1090.005BK => 250` etc? – nice_dev Mar 29 '21 at 09:05
  • @nice_dev, Yes exactly. ummm you have transformed a value into a key, it could be the solution, I hadn't thought about it – Marco Romano Mar 29 '21 at 09:06
  • 1
    Your sample array seems to have a fixed structure, so there should be no problem extracting the values you want. You don't need recursion and dynamic access. Two nested loops (one on orders, one on order items) should do the trick. – El_Vanja Mar 29 '21 at 09:21
  • @El_Vanja yes, I was looking something like this. find all the `[items]` and build an array with the sku as Key and Quantity as Value. `SKU => Quantity`. – Marco Romano Mar 29 '21 at 09:24
  • Marco, I 100% agree with @El_V. There is no reason to call a `recursive` function on your data. I would not use either answer in my own project. I cannot provide an answer until you provide sample input as `var_export()`. – mickmackusa Mar 29 '21 at 09:52

3 Answers3

2

array_walk_recursive is indeed the way to go, however, it gets a little tricky with PHP associative arrays as they are dictionaries and maintain keys in sequential order, meaning, keys order are the same as the way they were inserted in the first place. To make it work regardless of the order of quantity and sku keys, maintain global variables $sku and $qt and switch to the default values whenever you find their pair.

Snippet:

<?php

$sku = '';
$qt = -1;
$result = [];
    
array_walk_recursive($arr, function($v, $k) use(&$sku,&$qt,&$result) {
    if($k === 'quantity'){
        if($sku !== ''){
            $result[ $sku ] = $result[ $sku ] ?? 0;
            $result[ $sku ] += $v;
            $sku = '';
        }else {
            $qt = $v;
        }
    }elseif($k === 'sku'){
        if($qt !== -1){
            $result[ $v ] = $result[ $v ] ?? 0;
            $result[ $v ] += $qt;
            $qt = -1;
        }else{
            $sku = $v;
        }
    }
});
 
 
print_r($result);
nice_dev
  • 17,053
  • 2
  • 21
  • 35
  • 1
    Thanks so much, it works perfectly. This was too complex for my PHP knowledge. I understand the theory you explained to me, now I want to study what you did because I want to learn. Thanks again! – Marco Romano Mar 29 '21 at 09:38
  • 1
    `elseif` is one word in php to comply with PSR-12 guidelines. – mickmackusa Mar 29 '21 at 09:55
  • I just make a little change in: `$result[ $v ] += $qt;` With `+=` operation if there are more the one order with the same sku the quantity are update. – Marco Romano Mar 29 '21 at 10:02
  • @mickmackusa Thanks for letting me know. Updated. – nice_dev Mar 29 '21 at 10:51
  • @MarcoRomano Is there a possibility to have same SKUs multiple times? Aren't they unique per product? – nice_dev Mar 29 '21 at 10:52
  • @MarcoRomano Updated for different orders. – nice_dev Mar 29 '21 at 11:01
  • The reason that I would not endorse this script is because `array_walk_recursive()` is simply ill-suited for this task. It will be iterating all leaf nodes such as `transaction_id`, `date`, `currency`, `title`, `quantity`,`sku`, `price` **but you only actually want `quantity` and `sku`**. This equates to a lot of useless iterations. Not to mention that simple loops make for a much less convoluted solution. – mickmackusa Mar 29 '21 at 11:17
  • 1
    @mickmackusa You are speculating. What is the guarantee that `Orders` is at the first level of the array? Your solution assumes so but my solution works either way. – nice_dev Mar 29 '21 at 11:26
  • How preposterous. I have made an accurate observation explaining that your answer makes unnecessary iterations, and you defend your answer by saying the OP's sample data could be wrong. Okay, _I'm_ speculating. Here's hard proof of why researchers should not use your indirect approach on the provided data structure: https://3v4l.org/eLBmX It's cool if the green tick isn't moved; this isn't the first time on Stack Overflow that researchers will need to ignore the tick to find the best answer. – mickmackusa Mar 29 '21 at 13:15
  • @mickmackusa Don't get offended. I agree on the trivial iterations. But I am saying how are we sure the `Orders` is at the first level? Also, why don't you also review the other answer? Usually, why it is always me? I don't mind your interference because we usually have a productive exchange but I feel targeted at times when there are also other users answering. – nice_dev Mar 29 '21 at 13:30
  • Read all my comments on this page I targeted both answers in comment and my answer. I blow my whistle at everything so researchers can make the best, informed choices. – mickmackusa Mar 29 '21 at 13:32
  • @mickmackusa Ok, but my answer seems to have more comments usually. – nice_dev Mar 29 '21 at 13:35
2

Another alternative would be to use a combination of array_column to get all items first. Then merge all sub items into just one multi dimensional using array_merge_recursive with splat. And finally, again array_column to get the sku and quantity:

$items = array_column(array_merge_recursive(... array_column($data['Orders'], 'items')), 'quantity', 'sku');

In the end, you'll key-pair value with sku and quantity.

Kevin
  • 41,694
  • 12
  • 53
  • 70
  • here's a fiddle to see the [sample output](https://www.tehplayground.com/U8uE2ixEX2NpOJOc) – Kevin Mar 29 '21 at 09:35
  • Thank you @Kevin, This is another simple method that works very well. He hadn't thought about `array_merge_recursive`. – Marco Romano Mar 29 '21 at 09:41
  • This solution cannot be used to "sum" quantities where a given sku occurs more than once. For this reason, this answer is not suitable for this question which requires summing. Also, the use of `array_merge_recursive()` is unnecessary overhead -- to flatten the subarrays from different orders, you only need `array_merge()` & `splat`. Proof that recursion is unnecessary: https://3v4l.org/N5Z7g So basically, I'm saying that this answer is deceiving researchers with fancy functional programming. Not entirely Kevin's fault, I suppose, since the OP did not provide a better [mcve]. – mickmackusa Mar 29 '21 at 21:22
  • 1
    @mickmackusa true. and no worries, yes when I submitted this answer I already knew that this one-liner would not combine quantities if ever they are on the same nesting inside `items` index, so only on the assumption that the quantities on the items are already merged by the time they are in the OP's provided array. – Kevin Mar 30 '21 at 01:09
1

Recursive techniques are unnecessary overhead for this task. You do not need to visit every leaf-node to generate the desired output.

The other answers also do not bother to sum skus which are encountered more than once (in separate orders). Here is proof: https://3v4l.org/ZCRTu

I've expanded your sample data to include an sku which occurs more than once. This way you can see that quantities are actually summed where necessary.

Code: (Demo)

$result = [];
foreach ($data['Orders'] as $order) {
    foreach ($order['items'] as $item) {
        $result[$item['sku']] = ($result[$item['sku']] ?? 0) + $item['quantity'];
    }
}
var_export($result);

If you prefer functional programming, you will need to reduce the number of levels (in other words, use a classic flattening technique -- I prefer merge&spread) before you can group&sum.

Code: (Demo)

var_export(
    array_reduce(
        array_merge(
            ...array_column($data['Orders'], 'items')
        ),
        function($carry, $item) {
            $carry[$item['sku']] = ($carry[$item['sku']] ?? 0) + $item['quantity'];
            return $carry;
        }
    )
);

Output from either snippet:

array (
  '1T.810BK' => 200,
  '1090.005BK' => 550,
  '1.DIYBOX.ULT.638RE' => 3,
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136