skip to Main Content

I’m trying to output a multidimensional array as an HTML table with all possible combinations.

$attributes = [
  'size' => [
    'large',
    'medium',
    'small',
  ],
  'color' => [
    'red',
    'blue',
    'green',
  ],
  'material' => [
    'cotton',
    'polyester',
  ],
];

The output needs to be:

size color material
large red cotton
large red polyester
large blue cotton
etc. etc. etc.

At any point, this array could receive new sizes, colors, materials or a whole new attribute.

Thus far, I’ve checked various other questions, and I can’t find any that doesn’t specify that the multidimensional array has a fixed amount of columns.

2

Answers


  1. First, the easy part – outputting the table header:

    echo '<table>';
    echo '<thead>';
    echo '<tr>';
    foreach ($attributes as $title => $vals) {
        echo '<th>' . htmlspecialchars($title) . '</th>';
    }
    echo '</tr>';
    echo '</thead>';
    

    Then construct an output array $rows that will contain the Caretesian product of the lists. We build this up from right-to-left, in order to match your desired output – so we start with the variations of the rightmost set, then duplicate this and prepend the value of the next column leftwards, and so on, until we have parsed all the headings:

    // get variations of last column in separate rows
    $rows = array_chunk(array_pop($attributes), 1);
    
    // process each remaining column, right to left
    while ($lastCol = array_pop($attributes)) {
        $newRows = [];
        foreach ($lastCol as $val) {
            // duplicate whatever rows we already have
            // but prepend the value of the column we're processing
            foreach ($rows as $row) {
                array_unshift($row, $val);
                $newRows[] = $row;
            }
        }
        // store the newly constructed rows
        $rows = $newRows;
    }
    

    Then output the $rows array:

    echo '<tbody>';
    foreach ($rows as $row) {
        echo '<tr>';
        foreach ($row as $val) {
            echo '<td>' . htmlspecialchars($val) . '</td>';
        }
        echo '<tr>';
    }
    echo '</tbody>';
    echo '</table>';
    
    Login or Signup to reply.
  2. This question is effectively a two-part duplicate of previously posted content on Stack Overflow. While Rob’s answer is implementing the general best-practice of html encoding characters when printing, none of the sample data needs this protective step.

    Code: (Demo)

    // https://stackoverflow.com/a/44910370/2943403
    function cartesian(array $input): array
    {
        $result = [[]];
        foreach ($input as $key => $values) {
            $append = [];
            foreach ($values as $value) {
                foreach ($result as $data) {
                    $append[] = $data + [$key => $value];
                }
            }
            $result = $append;
        }
        return $result;
    }
    
    $attributes = [
      'size' => ['large', 'medium', 'small'],
      'color' => ['red', 'blue', 'green'],
      'material' => ['cotton', 'polyester'],
    ];
    
    // https://stackoverflow.com/a/2465109/2943403
    echo '<table border="1">';
        echo '<thead>';
            echo '<tr><th>' . implode('</th><th>', array_keys($attributes)) . '</th></tr>';
        echo '</thead>';
        echo '<tbody>';
            foreach (cartesian($attributes) as $row) {
                echo '<tr><td>' . implode('</td><td>', $row) . '</td></tr>';
            }
            echo '<tr>';
        echo '</tbody>';
    echo '</table>';
    

    Or to sort the output rows by size, then color, then material, Titus’s generator will suffice.

    Code: (Demo)

    // https://stackoverflow.com/a/39174062/2943403
    function acartesian(array $a)
    {
        if ($a) {
            $k = array_key_last($a);
            if ($u = array_pop($a)) {
                foreach (acartesian($a) as $p) {
                    foreach ($u as $v) {
                        yield $p + [$k => $v];
                    }
                }
            }
        } else {
            yield[];
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search