Search code examples
phparraysjsonmultidimensional-arraygrouping

Group array row on one column and form subarrays of varying depth/structure


I want to group rows in an array with the same color and then similar size data into special subarrays.

Sample array:

$array = [
    ['color'=>'#000000','size'=>'L','count'=>2],
    ['color'=>'#000000','size'=>'XL','count'=>1],
    ['color'=>'#ffffff','size'=>'L','count'=>2],
    ['color'=>'#ffffff','size'=>'XL','count'=>1],
    ['color'=>'#ff0000','size'=>'L','count'=>1]
];

And I want them to be like this JSON:

[{
   "color": "#000000",
   "size": [
      {
       "value": "L","count": 2
      },
      {
        "value": "Xl","count": 1
      }
    ]
  },
  {
    "color": "#ffffff",
    "size": [
      {
        "value": "L","count": 2
      },
      {
        "value": "Xl","count": 1
      }
    ]
  },
  {
    "color": "#ff0000",
    "size": "L",
    "count": 1
  }
]

Solution

  • UPDATE: Added a correction for the desired output at the bottom.

    This seems easy, but is rather difficult to do right. The best approach is to divide and conquer. First collect the data together, which belongs together, and then make the nice JSON output. My first step is therefore this:

    $input = [['color'=>'#000000','size'=>'L','count'=>2],
              ['color'=>'#000000','size'=>'XL','count'=>1],
              ['color'=>'#ffffff','size'=>'L','count'=>2],
              ['color'=>'#ffffff','size'=>'XL','count'=>1],
              ['color'=>'#ff0000','size'=>'L','count'=>1]];
              
    $colors = [];
    
    foreach ($input as $product) {
        extract($product);
        $colors[$color][$size] = $count;
    }
    
    echo json_encode($colors,  JSON_PRETTY_PRINT);
    

    This outputs:

    Array
    (
        [#000000] => Array
            (
                [L] => 2
                [XL] => 1
            )
    
        [#ffffff] => Array
            (
                [L] => 2
                [XL] => 1
            )
    
        [#ff0000] => Array
            (
                [L] => 1
            )
    
    )
    

    Please note that extract() should be used with care, since it can generate more variables than you bargained for, but it is very useful here. Never use it at the global scope, like I did here.

    And now we turn this into the wanted JSON, like this:

    $output = [];
    
    foreach ($colors as $color => $sizes) {
        $data = [];
        foreach ($sizes as $size => $count) {
            $data[] = ['value' => $size,
                       'count' => $count];
        }
        $output[] = ['color' => $color,
                     'size'  => $data];
    }
              
    echo json_encode($output,  JSON_PRETTY_PRINT);
    

    This outputs:

    [
        {
            "color": "#000000",
            "size": [
                {
                    "value": "L",
                    "count": 2
                },
                {
                    "value": "XL",
                    "count": 1
                }
            ]
        },
        {
            "color": "#ffffff",
            "size": [
                {
                    "value": "L",
                    "count": 2
                },
                {
                    "value": "XL",
                    "count": 1
                }
            ]
        },
        {
            "color": "#ff0000",
            "size": [
                {
                    "value": "L",
                    "count": 1
                }
            ]
        }
    ]
    

    You might want to put all this code inside a function.

    Here's a PHPFiddle.

    NOTE: It is important to note that this code assumes that there are no duplicate color/size combinations in the input array.

    In case the output you want wasn't a result of a typo, here's how you can modify the second loop to get it:

    $output = [];
    
    foreach ($colors as $color => $sizes) {
        $data = [];
        foreach ($sizes as $size => $count) {
            $data[] = ['value' => $size,
                       'count' => $count];
        }
        if (count($data) == 1) {
            $output[] = ['color' => $color,
                         'size'  => $data[0]['value'],
                         'count' => $data[0]['count']];
        } else {
            $output[] = ['color' => $color,
                         'size'  => $data];
        }                     
    }
              
    echo json_encode($output,  JSON_PRETTY_PRINT);
    

    See this PHPFiddle