1

I'm having a hard time manipulating an array of objects in PHP. I need to group the objects by id, while summing up the points.

Starting array of objects:

[
  {
    "id": "xx",
    "points": 25
  },
  {
    "id": "xx",
    "points": 40
  },
  {
    "id": "xy",
    "points": 40
  },
]

What I need:

[
  {
    "id": "xx",
    "points": 65
  },
  {
    "id": "xy",
    "points": 40
  },
]

As a frontender, I'm having a hard time with object/array manipulations in PHP. Any help would be greatly appreciated!

Thomas Miller
  • 115
  • 2
  • 16

4 Answers4

1
  • Parse JSON as Object
  • Aggregate Data
  • Put back as JSON
$json = <<<'_JSON'
[
  {
    "id": "xx",
    "points": 25
  },
  {
    "id": "xx",
    "points": 40
  },
  {
    "id": "xy",
    "points": 40
  }
]
_JSON;

$aggregate = [];
foreach(json_decode($json) as $data) {
    if(!isset($aggregate[$data->id])) $aggregate[$data->id] = 0;
    $aggregate[$data->id] += $data->points;
}

$output = [];
foreach($aggregate as $id => $points) {
    $output[] = ['id' => $id, 'points' => $points];
}

echo json_encode($output);
[{"id":"xx","points":65},{"id":"xy","points":40}]
Markus Zeller
  • 8,516
  • 2
  • 29
  • 35
1

i hope this answer help you first i will change objects to array and return the result to array again

 $values =[
  [
    "id"=> "xx",
    "points"=> 25
  ],
  [
    "id"=> "xx",
    "points"=> 40
  ],
  [
    "id"=> "xy",
    "points"=> 40
  ],
];
$res  = array();
foreach($values as $vals){
  if(array_key_exists($vals['id'],$res)){
    $res[$vals['id']]['points']    += $vals['points'];
    $res[$vals['id']]['id']        = $vals['id'];
  }
  else{
    $res[$vals['id']]  = $vals;
  }
}
$result = array();
foreach ($res as $item){
  $result[] = (object) $item;
}

output enter image description here

1

You may use array_reduce buil-in function to do the job. Also, when looping through the object's array (the callback), you should check if the result array has the current item's ID to verify that wether you need to add the item to the result array or to make the sum of points attributes.

Here's an example:

// a dummy class just to replicate the objects with ID and points attributes
class Dummy 
{
    public $id;
    public $points;
    
    public function __construct($id, $points)
    {
        $this->id = $id;
        $this->points = $points;
    }
}

// the array of objects
$arr = [new Dummy('xx', 25), new Dummy('xx', 40), new Dummy('xy', 40)];

// loop through the array
$res = array_reduce($arr, function($carry, $item) {
    // holds the index of the object that has the same ID on the resulting array, if it stays NULL means it should add $item to the result array, otherwise calculate the sum of points attributes
    $idx = null;
    // trying to find the object that has the same id as the current item
    foreach($carry as $k => $v)
        if($v->id == $item->id) {
            $idx = $k;
            break;
        }
    // if nothing found, add $item to the result array, otherwise sum the points attributes
    $idx === null ? $carry[] = $item:$carry[$idx]->points += $item->points;
    // return the result array for the next iteration
    return $carry;
}, []);

This will result in something like this:

array(2) {
  [0]=>
  object(Dummy)#1 (2) {
    ["id"]=>
    string(2) "xx"
    ["points"]=>
    int(65)
  }
  [1]=>
  object(Dummy)#3 (2) {
    ["id"]=>
    string(2) "xy"
    ["points"]=>
    int(40)
  }
}

Hope that helps, feel free to ask for further help.

ThS
  • 4,597
  • 2
  • 15
  • 27
0

Let's use a helper variable called $map:

$map = [];

Build your map:

foreach ($input => $item) {
    if (!isset($map[$item["id"]])) $map[$item["id"]] = 0;
     $map[$item["id"]] += $item["points"];
}

Now let's build the output:

$output = [];
foreach ($map as $key => $value) {
    $output[] = (object)["id" => $key, "points" => $value];
}
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175