Search code examples
phparraysloopsmultidimensional-arrayarray-column

Get column of data from third level of a multidimensional array


I have a multidimensional array with four levels of data. Here's a small sample:

$data = [
    [
        [
            'Date' => '2021-03-15T00:00:00.0000000+01:00',
            'Arena' => ['Id' => 181796, 'Name' => 'Motala bibliotek'],
            'Description' => null,
            'IsCanceled' => null
        ],
        [
            'Date' => '2021-03-16T00:00:00.0000000+01:00',
            'Arena' => ['Id' => 181796, 'Name' => 'Motala bibliotek'],
            'Description' => null,
            'IsCanceled' => null
        ],
        [
            'Date' => '2021-03-17T00:00:00.0000000+01:00',
            'Arena' => ['Id' => 181796, 'Name' => 'Motala bibliotek'],
            'Description' => null,
            'IsCanceled' => null
        ]
    ],
    [
        [
            'Date' => '2021-03-15T00:00:00.0000000+01:00',
            'Arena' => ['Id' => 181796, 'Name' => 'Motala bibliotek'],
            'Description' => null,
            'IsCanceled' => null
        ],
        [
            'Date' => '2021-03-16T00:00:00.0000000+01:00',
            'Arena' => ['Id' => 181796, 'Name' => 'Motala bibliotek'],
            'Description' => null,
            'IsCanceled' => null
        ]
    ]
];

I need to collect all of the Date values from the third level in the array.

I know how to loop through the top level, but I am at a loss for how to write the rest of the code to get the deeper Date elements.

for($i = 0; $i < count($data); $i++) {
    echo $i;
    echo "<br>";
}

Desired output:

array (
  0 => '2021-03-15T00:00:00.0000000+01:00',
  1 => '2021-03-16T00:00:00.0000000+01:00',
  2 => '2021-03-17T00:00:00.0000000+01:00',
  3 => '2021-03-15T00:00:00.0000000+01:00',
  4 => '2021-03-16T00:00:00.0000000+01:00',
)

Solution

  • There are several ways to extract a column of data from the third level of a multidimensional array. Different techniques will have trade-offs. The following demonstrations will all generate the same output. (Demo Link)

    Eliminate top level of structure with spread operator before isolating columns

    • array_column(array_merge(...$data), 'Date')
      
    • Pros:
      • it is the most concise option
      • it does not iterate any data unnecessarily
      • array_column() doesn't need to check if the column key exists to avoid warnings
      • it perfectly fits into a codebase using functional stylings -- IOW, the return value is immediately accessible and no extra variable needs to be declared
    • Cons:
      • if you, your development team, or your future development team are not familiar with variadics, then this one-liner is not very intuitive. On the other hand, raising awareness of how to flattening an array will be good for your team; maybe just write a note in your code and link to a SO page or the php docs for quick reference.

    Check leaf-node keys recursively (not recommended)

    • $result = [];
      array_walk_recursive(
          $data,
          function($v, $k) use (&$result) {
              if ($k === 'Date') {
                  $result[] = $v;
              }
          }
      );
      
    • Pros:
      • because array_walk_recursive() traverses leaf-nodes and 'Data' is always "scalar", all Date elements will be found
      • if the level structure changes, the code will still work because array_walk_recursive is level-ignorant
    • Cons:
      • array_walk_recursive() traverses leaf-nodes, it will visit ALL scalar elements in the structure which means there will be some useless processing
      • not very concise and involves an anonymous function which requires $result to be declared as a modifiable-by-reference variable
      • if the data type of Date changes to something iterable, then it ceases to be a leaf-node

    Loop and push elements columns using the spreading operator

    • $result = [];
      foreach ($data as $group) {
          array_push($result, ...array_column($group, 'Date'));
      }
      var_export($result);
      
    • Pros:
      • it is not too verbose and does not involve an anonymous function (so no modifiable-by-reference variable)
      • it does not iterate any data unnecessarily
    • Cons:
      • it doesn't lend itself to a functional programming style because a $result variable must be declared
      • if you, your development team, or your future development team are not familiar with variadics; maybe just write a note in your code and link to a SO page or the php docs for quick reference.

    Classic nested foreach loops

    • $result = [];
      foreach ($data as $group) {
          foreach ($group as $row) {
              if (array_key_exists('Date', $row)) {
                  $result[] = $row['Date'];
              }
          }
      }
      
    • Pros:
      • probably the most intuitive/semantic for developers of any experience level
      • it does not iterate any data unnecessarily
      • it has minimal functional overhead* so it will potentially perform the best on huge data sets (be sure to benchmark if this is a reasonable concern)
    • Cons:
      • it is completely procedural style and requires a $result variable to be declared
      • it is relatively verbose
    • *The condition with array_key_exists() will not be necessary if the input data is guaranteed to contain the Date element on all levels. If they are not guaranteed to exist, then the condition serves to prevent warnings.

    A modern spin on classic nested loops

    • $result4 = [];
      foreach ($data as $group) {
          foreach ($group as ['Date' => $result4[]]);
      }
      
    • Pros:
      • Using array destructuring to access only the date value in the inner loop makes the snippet more succinct
      • it does not iterate any data unnecessarily
      • it has minimal functional overhead* so it will potentially perform the best on huge data sets (be sure to benchmark if this is a reasonable concern)
    • Cons:
      • it is completely procedural style and requires a $result variable to be declared
      • it is less commin to see array destructuring in a foreach() as well as a body-less loop pushing data into a variable so your development team may not be familiar with the syntax.
      • the Date element must exist on all levels.