2

I have an array that looks like this:

array:3 [
  0 => array:5 [
    "id" => 18
    "product_id" => 37
    "total_price" => "643.00"
    "created_at" => "2017-05-02 13:22:35"
    "updated_at" => "2017-05-02 13:22:35"
  ]
  1 => array:5 [
    "id" => 19
    "product_id" => 36
    "total_price" => "532.00"
    "created_at" => "2017-05-02 13:23:47"
    "updated_at" => "2017-05-02 13:23:47"
  ]
  2 => array:5 [
    "id" => 20
    "product_id" => 36
    "total_price" => "532.00"
    "created_at" => "2017-05-03 13:20:47"
    "updated_at" => "2017-05-03 13:20:47"
  ]
]

In my case if the key "product_id" has the same value I want to reduce merge those arrays and add the total_price but I have to also save a new key to the new array: $quantity to store the number of array with the same key that existed before merging. So the final result should look like this:

array:2 [
  0 => array:5 [
    "id" => 18
    "product_id" => 37
    "total_price" => "643.00"
    "quantity"   => 1
    "created_at" => "2017-05-02 13:22:35"
    "updated_at" => "2017-05-02 13:22:35"
  ]
  1 => array:5 [
    "id" => 19
    "product_id" => 36
    "total_price" => "1064.00"
    "quantity"   => 2
    "created_at" => "2017-05-02 13:23:47"
    "updated_at" => "2017-05-02 13:23:47"
  ]
]

Initially I tried with array_walk_recursive, but I got confused.

array_walk_recursive($products, function($value, $key) use (&$products_sold){
            array_push($products_sold, isset($products_sold[$key]) ?  $value + $products_sold[$key] : $value);
        });

I know this is useless but I failed to realize how to do this. Any explanation would be helpful. Thank you all for your time!

2 Answers 2

2

Try

$result = array_reduce($products, function ($result, $product) {
    foreach ($result as $index => $value) {
        if ($value['product_id'] == $product['product_id']) {
            $result[$index]['total_price'] += $product['total_price'];

            return $result;
        }
    }
    $result[] = $product;
    return $result;
}, []);

But I strongly recomment use BCMath functions instead of working with floats.

So its better to write

$result[$index]['total_price'] = bcadd($result[$index]['total_price'], $product['total_price'], 2);

instead of

$result[$index]['total_price'] += $product['total_price'];

Update

We can rewrite above version to nested foreach like so:

$reducer = function ($result, $product) {
    foreach ($result as $index => $value) {
        if ($value['product_id'] == $product['product_id']) {
            $result[$index]['total_price'] += $product['total_price'];

            return $result;
        }
    }
    $result[] = $product;
    return $result;
};

$result = [];

foreach($products as $product) {
    $result = $reducer($result, $product);
}

so we can see it is just foreach loop which calls function which contains another foreach. We need another foreach because we need the way to determine if array contains product_id. We can index result with product_id so we can avoid secind foreach:

$result = array_reduce($products, function ($result, $product) {
    $productId = $product['product_id'];
    if(isset($result[$productId])) {
        $result[$productId]['total_price'] += $product['total_price'];
        return $result;
    }
    $result[$productId] = $product;
    return $result;
}, []);

and then if you need indexes to be ordered from zero just call array_values($result)

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

3 Comments

sadly I get: Invalid argument supplied for foreach() @Nikita
little fix added :)
It works perffectly now, thank you! But can you explain to me a bit the part with result please? I mean you make result equal with the array_reduce(which return the products reduced?) and inside of it you loop through $result...that's the part that I don't get.
2

Got it done! I should say I didnt use total_price as string so I can do addition.

$stuff = array(
    array(
        "id" => 18,
        "product_id" => 37,
        "total_price" => 643.00,
        "created_at" => "2017-05-02 13:22:35",
        "updated_at" => "2017-05-02 13:22:35"),

    array(
        "id" => 19,
        "product_id" => 36,
        "total_price" => 532.00,
        "created_at" => "2017-05-02 13:23:47",
        "updated_at" => "2017-05-02 13:23:47"),

    array(
        "id" => 20,
        "product_id" => 36,
        "total_price" => 532.00,
        "created_at" => "2017-05-03 13:20:47",
        "updated_at" => "2017-05-03 13:20:47")
    );

for ($i=0; $i < count($stuff); $i++) { 
    $product_id = $stuff[$i]["product_id"];
    foreach ($stuff as $key => $value) {
        if($value["product_id"] === $product_id && $i !== $key) {
            $stuff[$i]["total_price"] += $value["total_price"];
            unset($stuff[$key]);
        }
    }
}

4 Comments

Undefined offset: 2..that is for the $product_id apparently while looping it gets out of bounds
Huh this works for me and I cant see why you should get out of bounds...
I don't think intval is safe to use here, because you'll lose cents
@NikitaU. Yeah I dont even use it anymore because in his example total_price is a string thats why I used intval but now that I use number for total_price, I can remove intval.

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.