Вы можете добавить свой собственный обработчик ошибок, который может предоставить дополнительную информацию об отладке. Кроме того, вы можете настроить его для отправки по электронной почте.
function ERR_HANDLER($errno ,$errstr, $errfile, $errline){
$msg="<b>Someting bad happened.</b> [$errno] $errstr <br><br>
<b>File:</b> $errfile <br>
<b>Line:</b> $errline <br>
<pre>".json_encode(debug_backtrace(), JSON_PRETTY_PRINT)."</pre> <br>";
echo $msg;
return false;
}
function EXC_HANDLER($exception){
ERR_HANDLER(0,$exception->getMessage(),$exception->getFile(),$exception->getLine());
}
function shutDownFunction() {
$error = error_get_last();
if ($error["type"] == 1) {
ERR_HANDLER($error["type"],$error["message"],$error["file"],$error["line"]);
}
}
set_error_handler ("ERR_HANDLER", E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
register_shutdown_function("shutdownFunction");
set_exception_handler("EXC_HANDLER");
Я разбил это на два этапа: 1. сгенерировать древовидную структуру из входных данных, чтобы позволить подсчет числа строк; 2. Рисование строк и столбцов таблицы.
Весь код настолько универсален, насколько это возможно, чтобы избежать жестко закодированного количества столбцов.
Я также добавил динамический заголовок таблицы. Если вы добавите еще один столбец в структуру данных, заголовки таблицы HTML также будут обновлены. Например, можно использовать этот же код для добавления четвертого уровня, например, "LEVEL_4": "LEVEL 1.2.2.1"
в один или несколько рядов.
var data = [
{
"LEVEL_1": "LEVEL 1",
"LEVEL_2": "LEVEL 1.1",
"LEVEL_3": "LEVEL 1.1.1"
},
{
"LEVEL_1": "LEVEL 1",
"LEVEL_2": "LEVEL 1.2",
"LEVEL_3": "LEVEL 1.2.1"
},
{
"LEVEL_1": "LEVEL 1",
"LEVEL_2": "LEVEL 1.2",
"LEVEL_3": "LEVEL 1.2.2"
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.1",
"LEVEL_3": "LEVEL 2.1.1"
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.1",
"LEVEL_3": "LEVEL 2.1.2"
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.2",
"LEVEL_3": "LEVEL 2.2.1"
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.2",
"LEVEL_3": "LEVEL 2.2.2"
}
];
/**
* Generate rows and columns for the table using the JSON data.
*
* @param {Object} data
* @param {HTMLTableSectionElement} tableBody
* @param {HTMLTableSectionElement} tableHead
*/
function setTableContent(data, tableBody, tableHead) {
/* The rowspan is stored here depending on the label of each column */
var columnRowspans = {};
var columnHeadings = {};
/**
* Translate the data into a tree-like structure
*
* @param {JSON} data
* @return {Array}
*/
function translateData(data) {
var rows = [];
/* Iterate over each row in the dataset */
data.forEach(function (row) {
var columns = [],
label;
/* Find the individual columns */
for (var key in row) {
/* Store the columns header */
columnHeadings[key] = true;
label = row[key];
/* Skip those columns that were already added */
if (columnRowspans[label]) {
columnRowspans[label]++;
continue;
}
columns.push(label);
columnRowspans[label] = 1;
}
rows.push(columns);
});
return rows;
}
/* Template string used for a single field in the table */
var cellTemplate = '<td rowspan="{rowspan}">{content}</td>';
/* Output */
var html = '';
translateData(data).forEach(function (row, index) {
html += '<tr>';
row.forEach(function (columnLabel) {
/* Use the stored rowspans here to generate columns */
html += cellTemplate
.replace('{rowspan}', columnRowspans[columnLabel])
.replace('{content}', columnLabel);
});
html += '</tr>';
});
if (tableBody instanceof HTMLTableSectionElement) {
tableBody.innerHTML = html;
}
if (tableHead instanceof HTMLTableSectionElement) {
var thead = '<tr>';
Object.keys(columnHeadings).forEach(function (heading) {
thead += '<th>' + heading.replace('_', ' ') + '</th>';
});
thead += '</tr>';
tableHead.innerHTML = thead;
}
}
setTableContent(data, document.querySelector('#user tbody'), document.querySelector('#user thead'));
table {
border-collapse: collapse;
}
td {
padding: 20px;
border: 1px solid black;
text-align: center;
}
th {
padding: 20px;
border: 1px solid black;
text-align: center;
}
<table id="user">
<thead>
</thead>
<tbody>
</tbody>
</table>
С помощью этого вопроса мне удалось объединить 1-й уровень.
var data = [{
"LEVEL_1": "LEVEL 1",
"LEVEL_2": "LEVEL 1.1",
"LEVEL_3": "LEVEL 1.1.1",
},
{
"LEVEL_1": "LEVEL 1",
"LEVEL_2": "LEVEL 1.2",
"LEVEL_3": "LEVEL 1.2.1",
},
{
"LEVEL_1": "LEVEL 1",
"LEVEL_2": "LEVEL 1.2",
"LEVEL_3": "LEVEL 1.2.2",
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.1",
"LEVEL_3": "LEVEL 2.1.1",
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.1",
"LEVEL_3": "LEVEL 2.1.2",
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.2",
"LEVEL_3": "LEVEL 2.2.1",
},
{
"LEVEL_1": "LEVEL 2",
"LEVEL_2": "LEVEL 2.2",
"LEVEL_3": "LEVEL 2.2.2",
}
];
for (i = 0; i < data.length; i++) {
var l1 = data[i].LEVEL_1;
data[i].rowspan = 1;
for (j = i + 1; j < data.length; j++) {
var l2 = data[j].LEVEL_1;
if (l1 == l2) {
data[i].rowspan += 1;
} else {
break;
}
}
i = j - 1;
}
var tableStr = '';
$.each(data, function(index, value) {
if (value.rowspan > 1) {
tableStr += '<tr>' +
'<td rowspan="' + value.rowspan + '">' + value.LEVEL_1 + '</td>' +
'<td>' + value.LEVEL_2 + '</td>' +
'<td>' + value.LEVEL_3 + '</td>' +
'</tr>';
} else {
tableStr += '<tr>' +
'<td>' + value.LEVEL_2 + '</td>' +
'<td>' + value.LEVEL_3 + '</td>' +
'</tr>';
}
});
$('#user tbody').html(tableStr);
table {
border-collapse: collapse;
}
td {
padding: 20px;
border: 1px solid black;
text-align: center;
}
th {
padding: 20px;
border: 1px solid black;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="user">
<thead>
<tr>
<th>LEVEL 1</th>
<th>LEVEL 2</th>
<th>LEVEL 3</th>
</tr>
<tr>
</tr>
</thead>
<tbody>
</tbody>
</table>
У меня есть решение для вас. Возможно, он не самый элегантный, но он работает
Сначала я преобразую данные из JSON во вложенный объект, который считает появление элементов более низкого уровня .
Вторым этапом является прохождение этого вложенного объекта и создание таблицы, tr и td соответственно со значениями счетчика из шага 1, используемого в качестве значений атрибута rowspan
.
Мне даже не нужен jQuery, только VanillaJS.
console.clear();
var data = [
{
LEVEL_1: "LEVEL 1",
LEVEL_2: "LEVEL 1.1",
LEVEL_3: "LEVEL 1.1.1"
},
{
LEVEL_1: "LEVEL 1",
LEVEL_2: "LEVEL 1.2",
LEVEL_3: "LEVEL 1.2.1"
},
{
LEVEL_1: "LEVEL 1",
LEVEL_2: "LEVEL 1.2",
LEVEL_3: "LEVEL 1.2.2"
},
{
LEVEL_1: "LEVEL 2",
LEVEL_2: "LEVEL 2.1",
LEVEL_3: "LEVEL 2.1.1"
},
{
LEVEL_1: "LEVEL 2",
LEVEL_2: "LEVEL 2.1",
LEVEL_3: "LEVEL 2.1.2"
},
{
LEVEL_1: "LEVEL 2",
LEVEL_2: "LEVEL 2.2",
LEVEL_3: "LEVEL 2.2.1"
},
{
LEVEL_1: "LEVEL 2",
LEVEL_2: "LEVEL 2.2",
LEVEL_3: "LEVEL 2.2.2"
}
];
const transformData = (data) => {
let t = {};
data.forEach((item, index, source) => {
let l1 = item.LEVEL_1,
l2 = item.LEVEL_2,
l3 = item.LEVEL_3;
t[l1] = t[l1] || {text: l1, content: {}, count: 0};
t[l1].content[l2] = t[l1].content[l2] || {text: l2, content: {}, count: 0};
t[l1].content[l2].content[l3] = t[l1].content[l2].content[l3] || {text: l3, content: {}, count: 0};
t[l1].count++
t[l1].content[l2].count++
t[l1].content[l2].content[l3].count++
})
return t;
}
const transformDataTable = (tdata) => {
const table = document.createElement('table');
for (l1 in tdata) {
const td1 = document.createElement('th')
td1.textContent = tdata[l1].text;
td1.setAttribute('rowspan', tdata[l1].count)
let done1 = false;
for (l2 in tdata[l1].content) {
const td2 = document.createElement('td')
td2.textContent = tdata[l1].content[l2].text;
td2.setAttribute('rowspan', tdata[l1].content[l2].count)
let done2 = false;
for (l3 in tdata[l1].content[l2].content) {
const td3 = document.createElement('td')
td3.textContent = tdata[l1].content[l2].content[l3].text;
td3.setAttribute('rowspan', tdata[l1].content[l2].content[l3].count)
const tr = document.createElement('tr')
!done1 && tr.appendChild(td1) && (done1 = true);
!done2 && tr.appendChild(td2) && (done2 = true);
tr.appendChild(td3);
table.appendChild(tr);
}
}
}
return table;
}
const tdata = transformData(data);
const table = transformDataTable(tdata);
document.body.appendChild(table)
table {
border-collapse: collapse;
}
td,
th {
padding: 20px;
border: 1px solid black;
text-align: center;
}
С некоторыми изменениями вы можете отсортировать каждый столбец по своему усмотрению
console.clear();
var data = [
{
LEVEL_1: "LEVEL 3",
LEVEL_2: "LEVEL 3.1",
LEVEL_3: "LEVEL 3.1.1"
},
{
LEVEL_1: "LEVEL 1",
LEVEL_2: "LEVEL 1.1",
LEVEL_3: "LEVEL 1.1.1"
},
{
LEVEL_1: "LEVEL 1",
LEVEL_2: "LEVEL 1.2",
LEVEL_3: "LEVEL 1.2.1"
},
{
LEVEL_1: "LEVEL 1",
LEVEL_2: "LEVEL 1.2",
LEVEL_3: "LEVEL 1.2.2"
},
{
LEVEL_1: "LEVEL 2",
LEVEL_2: "LEVEL 2.1",
LEVEL_3: "LEVEL 2.1.1"
},
{
LEVEL_1: "LEVEL 3",
LEVEL_2: "LEVEL 3.1",
LEVEL_3: "LEVEL 3.1.2"
},
{
LEVEL_1: "LEVEL 2",
LEVEL_2: "LEVEL 2.1",
LEVEL_3: "LEVEL 2.1.2"
},
{
LEVEL_1: "LEVEL 2",
LEVEL_2: "LEVEL 2.2",
LEVEL_3: "LEVEL 2.2.1"
},
{
LEVEL_1: "LEVEL 2",
LEVEL_2: "LEVEL 2.2",
LEVEL_3: "LEVEL 2.2.2"
},
{
LEVEL_1: "LEVEL 2",
LEVEL_2: "LEVEL 2.2",
LEVEL_3: "LEVEL 2.2.3"
}
];
const transformData = (data) => {
let t = {};
data.forEach((item, index, source) => {
let l1 = item.LEVEL_1,
l2 = item.LEVEL_2,
l3 = item.LEVEL_3;
t[l1] = t[l1] || {text: l1, content: {}, count: 0};
t[l1].content[l2] = t[l1].content[l2] || {text: l2, content: {}, count: 0};
t[l1].content[l2].content[l3] = t[l1].content[l2].content[l3] || {text: l3, content: {}, count: 0};
t[l1].count++
t[l1].content[l2].count++
t[l1].content[l2].content[l3].count++
})
return t;
}
const transformDataTable = (tdata, sort = (a,b) => b-a, sort1 = sort, sort2 = sort1) => {
const table = document.createElement('table');
for (l1 of Object.keys(tdata).sort(sort)) {
const td1 = document.createElement('th')
td1.textContent = tdata[l1].text;
td1.setAttribute('rowspan', tdata[l1].count)
let done1 = false;
for (l2 of Object.keys(tdata[l1].content).sort(sort1)) {
const td2 = document.createElement('td')
td2.textContent = tdata[l1].content[l2].text;
td2.setAttribute('rowspan', tdata[l1].content[l2].count)
let done2 = false;
for (l3 of Object.keys(tdata[l1].content[l2].content).sort(sort2)) {
const td3 = document.createElement('td')
td3.textContent = tdata[l1].content[l2].content[l3].text;
td3.setAttribute('rowspan', tdata[l1].content[l2].content[l3].count)
const tr = document.createElement('tr')
!done1 && tr.appendChild(td1) && (done1 = true);
!done2 && tr.appendChild(td2) && (done2 = true);
tr.appendChild(td3);
table.appendChild(tr);
}
}
}
return table;
}
const asc = (a,b) => b-a
const desc = (a,b) => a-b
const tdata = transformData(data);
const table = transformDataTable(tdata, desc, asc);
document.body.appendChild(table)
table {
border-collapse: collapse;
}
td,
th {
padding: 20px;
border: 1px solid black;
text-align: center;
}