Passed
Push — master ( 1e77ab...d0f64e )
by Matthew
06:39
created

Row::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 6
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MySociety\TheyWorkForYou\DataClass;
6
7
class Cell {
8
    public string $key;
9
    public $value;
10
11
    public function getValue() {
12
        return $this->value;
13
    }
14
}
15
16
class Row {
17
    /** @var Cell[] $cells */
18
    public array $cells = [];
19
20
    public function __construct(array $data) {
21
        foreach ($data as $key => $value) {
22
            $cell = new Cell();
23
            $cell->key = (string) $key;
24
            $cell->value = $value;
25
            $this->cells[$key] = $cell;
26
        }
27
    }
28
29
    public function get(string $column) {
30
        $cell = $this->cells[$column] ?? null;
31
        return $cell ? $cell->getValue() : null;
32
    }
33
34
    public function toArray(): array {
35
        $array = [];
36
        foreach ($this->cells as $cell) {
37
            $array[$cell->key] = $cell->value;
38
        }
39
        return $array;
40
    }
41
}
42
43
class DataFrame {
44
    /**
45
     * @var Row[] $rows An array of Row objects
46
     * @var string[] $columns An array of strings representing column names
47
     */
48
    public array $rows = [];
49
    public array $columns = [];
50
51
    public function __construct(array $data) {
52
        /**
53
         * Accepts an array of associative arrays, where each associative array represents a row in the DataFrame.
54
         * From this extract the column names and create a Row object for each row.
55
         */
56
        foreach ($data as $rowData) {
57
            // update the columns with any new keys in each row
58
            $this->columns = array_unique(array_merge($this->columns, array_keys($rowData)));
59
            // make sure all columns are strings rather than ints
60
            $this->rows[] = new Row($rowData);
61
        }
62
    }
63
64
    public function toArray(): array {
65
        return array_map(fn($row) => $row->toArray(), $this->rows);
66
    }
67
68
    public function toHTML(?string $url_column = null): string {
69
        // Generate a HTML table from the DataFrame
70
        $html = '<div style="overflow-x: auto; white-space: nowrap;">';
71
        $html .= '<table class="df-table">';
72
        // Add table headers
73
        $html .= '<tr class="df-row">';
74
        foreach ($this->columns as $column) {
75
            if ($url_column === $column) {
76
                continue;
77
            }
78
            $html .= '<th class="df-header">' . htmlspecialchars($column) . '</th>';
79
        }
80
        $html .= '</tr>';
81
        // Add table rows
82
        foreach ($this->rows as $row) {
83
            $url_value = $url_column ? $row->get($url_column) : null;
84
            $html .= '<tr class="df-row">';
85
            foreach ($this->columns as $index => $column) {
86
                if ($url_column === $column) {
87
                    continue;
88
                }
89
                $raw_cell_value = $row->get($column);
90
                $cell_value = htmlspecialchars((string) $raw_cell_value);
91
                $data_classes = ['df-data'];
92
                if (is_numeric($raw_cell_value)) {
93
                    $data_classes[] = 'df-data--number';
94
                }
95
96
                if ($url_value && $index === 0) {
97
                    // wrap first value in url
98
                    $cell_value = '<a href="' . $url_value . '">' . $cell_value . '</a>';
99
                }
100
                $html .= '<td class="' . implode(' ', $data_classes) . '">' . $cell_value . '</td>';
101
            }
102
            $html .= '</tr>';
103
        }
104
        $html .= '</table>';
105
        $html .= '</div>';
106
        return $html;
107
    }
108
}
109