Skip to content
Advertisement

Better way to iterate multidimensional array of unknown complexity from API than a dozen foreach loops in PHP

I am getting an array from an API that varies in number of levels but follows the same basic structure – here is a truncated sample as this particular repsonse is 25K lines:

{
 "Rows": {
  "Row": [
   {
    "Header": {
     "ColData": [
      {
       "value": "Ordinary Income/Expenses"
      },
      {
       "value": ""
      }
     ]
    },
    "Rows": {
     "Row": [
      {
       "Rows": {},
       "Summary": {
        "ColData": [
         {
          "value": "Gross Profit"
         },
         {
          "value": ""
         }
        ]
       },
       "type": "Section"
      },
      {
       "Header": {
        "ColData": [
         {
          "value": "Income"
         },
         {
          "value": ""
         }
        ]
       },
       "Rows": {
        "Row": [
         {
          "Header": {
           "ColData": [
            {
             "value": "40000 Sales Income",
             "id": "31"
            },
            {
             "value": ""
            }
           ]
          },
          "Rows": {
           "Row": [
            {
             "Rows": {
              "Row": [
               {
                "ColData": [
                 {
                  "value": "2022-01-24"
                 },
                 {
                  "value": "Invoice",
                  "id": "148774"
                 },
                 {
                  "value": "208232"
                 },
                 {
                  "value": "Hyatt:#211102",
                  "id": "7568"
                 },
                 {
                  "value": "JN",
                  "id": "4100000000000368107"
                 },
                 {
                  "value": "CAPTIVE AIRE"
                 },
                 {
                  "value": "11000 Accounts Receivable",
                  "id": "80"
                 },
                 {
                  "value": "38748.00"
                 },
                 {
                  "value": "38748.00"
                 }
                ],
                "type": "Data"
               },

I need to traverse the json, and where there is data in both [Header][ColData][value] AND [Header][ColData][id] extract the value, id (in this snippet “value”: “40000 Sales Income”, “id”: “31”) and the data that immediately follows the “value”/”id” in [Rows][Row][Rows][Row][ColData] (in this snippet starting with “ColData”: [{“value”: “2022-01-24″…)

[Rows][Row][Rows][Row][ColData] will have one to a few hundred subarrays. I can extract the data from the subarrays once they are found – it’s just managing the varying depths of the array that is warping my brain.

[Rows][Row][Rows][Summary] can be discarded as well.

I have tried multiple foreach loops – but by time I get 5 or 6 levels deep it gets very confusing. The number of Header sections varies depending on the report type. The [Rows][Row] nesting is multiple layers deep… I’m sure there has to be a better way than nesting foreach loops…

Advertisement

Answer

This is what I’ve come up with. Kindly modify it to meet your need.

$array = json_decode($json, true);

function  traverse_some_array($array){
    $result = [];
    foreach($array['Rows']['Row'] ?? [] as $row){
        if( !empty($row['Header']['ColData']) ){
            foreach($row['Header']['ColData'] as $coldata){
                if( isset($coldata['value'], $coldata['id']) ){
                    // there is data in both [Header][ColData][value] AND [Header][ColData][id]

                    // extract the value, id (in this snippet "value": "40000 Sales Income", "id": "31")
                    $extract_data = [
                        'value' => $coldata['value'],
                        'id'    => $coldata['id']
                    ];

                    // the data that immediately follows the "value"/"id" in [Rows][Row][Rows][Row][ColData]
                    $immediate_coldata = $row['Rows']['Row'] ?? [];

                    // you can do what ever you want with the results, eg return it or push it to a result array
                    $result[] = [
                        'extract_data' => $extract_data,
                        'immediate_coldata' => $immediate_coldata
                    ];
                }else{
                    // continue traversing the array
                    $result = array_merge($result,  traverse_some_array($row));
                }
            }
        }
    }
    return $result;
}

print_r(traverse_some_array($array));
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement