I have a JSON object representing a set of segments and I'd like to create an HTML table that compares each segment in the following format:
-------------------------------------------------------------------------------
domain group | vertical | measure | Segment 1 | Segment 2
-------------------------------------------------------------------------------
group 1 | all | users clicking | 340 | 340
| | % users opening | 10% | 10%
----------------------------------------------------------------
| cars | users clicking | 340 | 340
| | % users opening | 10% | 10%
-------------------------------------------------------------------------------
group 2 | all | users clicking | 340 | 340
| | % users opening | 10% | 10%
----------------------------------------------------------------
| cars | users clicking | 340 | 340
| | % users opening | 10% | 10%
-------------------------------------------------------------------------------
How can I create a row for each segment value that will display the value's measure and then group these by vertical and then by domain group?
JSON:
{
"set": [
{
"id": 1,
"segment_desc": "Segment 1",
"segment_summary": [
{
"section_type": "domain group",
"section_value": "group 1",
"children": [
{
"children": [
{
"format": "float",
"measure": "users clicking",
"value": 340
},
{
"format": "float",
"measure": "% users opening",
"value": 10
}
],
"section_type": "vertical",
"section_value": "all"
},
{
"children": [
{
"format": "float",
"measure": "users clicking",
"value": 340
},
{
"format": "float",
"measure": "% users opening",
"value": 10
}
],
"section_type": "vertical",
"section_value": "cars"
}
]
}
]
},
{
"id": 2,
"segment_desc": "Segment 2",
"segment_summary": [
{
"section_type": "domain group",
"section_value": "group 2",
"children": [
{
"children": [
{
"format": "float",
"measure": "users clicking",
"value": 340
},
{
"format": "float",
"measure": "% users opening",
"value": 1.24
}
],
"section_type": "vertical",
"section_value": "all"
},
{
"children": [
{
"format": "float",
"measure": "users clicking",
"value": 340
},
{
"format": "float",
"measure": "% users opening",
"value": 10
}
],
"section_type": "vertical",
"section_value": "cars"
}
]
}
]
}
]
}
EDIT: An acceptable alternative to the above table layout would be to print a row for each compared segment value without it being grouped by domain group and vertical:
domain group | vertical | measure | segment 1 | segment 2
-------------------------------------------------------------------------------
group 1 | all | users clicking | 340 | 340
-------------------------------------------------------------------------------
group 1 | all | % users opening | 10% | 10%
-------------------------------------------------------------------------------
group 1 | cars | users clicking | 340 | 340
-------------------------------------------------------------------------------
group 1 | cars | % users opening | 10% | 10%
-------------------------------------------------------------------------------
group 2 | all | users clicking | 340 | 340
I have jQuery and lodash libraries installed and either library can be used.
To create a table in HTML, you will need to define the table with the <table> tag. The <table> tag is the container of the table that specifies where the table will begin and where it ends. You can add a table row with the <tr> tag. To add a table header, we will need to use the <th> tag.
The <tr> HTML element defines a row of cells in a table. The row's cells can then be established using a mix of <td> (data cell) and <th> (header cell) elements.
Try this (pattern)
html
<table>
<th>Groups</th>
<tr class="group-1">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="group-1">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="group-2">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="group-2">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
js
// json, i.e.g., `{ "groups" : [{..}] }`
var Groups = {
"groups": [{
"group": {
"id": 1,
"description" : "clicks",
"all": [{
"segment": 10
}, {
"segment": 20
}],
"cars": [{
"segment": 30
}, {
"segment": 40
}]
}
}, {
"group": {
"id": 2,
"description" : "clicks",
"all": [{
"segment": 50
}, {
"segment": 60
}],
"cars": [{
"segment": 70
}, {
"segment": 80
}]
}
}]
};
$.each(Groups, function (key, value) {
var g1all = $("tr.group-1:eq(0) > td");
var g1cars = $("tr.group-1:eq(1) > td");
var g2all = $("tr.group-2:eq(0) > td");
var g2cars = $("tr.group-2:eq(1) > td");
$(g1all).eq(0).html("Group: " + value[0].group.id);
$(g1all).eq(1).html("Vertical: "
+ ("all" in value[0].group ? "All" : "error")
+" Measure: "+ value[0].group.description);
$(g1all).eq(2).html("Segment 1: " + value[0].group.all[0].segment);
$(g1all).eq(3).html(" Segment 2: " + value[0].group.all[1].segment);
$(g1cars).eq(0).html("Group: " + value[0].group.id);
$(g1cars).eq(1).html("Vertical: "
+ ("cars" in value[0].group ? "Cars" : "error")
+" Measure: "+ value[0].group.description);
$(g1cars).eq(2).html("Segment 1: " + value[0].group.cars[0].segment);
$(g1cars).eq(3).html(" Segment 2: " + value[0].group.cars[1].segment);
$(g2all).eq(0).html("Group: " + value[1].group.id);
$(g2all).eq(1).html("Vertical: "
+ ("all" in value[1].group ? "All" : "error")
+" Measure: "+ value[1].group.description);
$(g2all).eq(2).html("Segment 1: " + value[1].group.all[0].segment);
$(g2all).eq(3).html(" Segment 2: " + value[1].group.all[1].segment);
$(g2cars).eq(0).html("Group: " + value[1].group.id);
$(g2cars).eq(1).html("Vertical: "
+ ("cars" in value[1].group ? "Cars" : "error")
+" Measure: "+ value[1].group.description);
$(g2cars).eq(2).html("Segment 1: " + value[1].group.cars[0].segment);
$(g2cars).eq(3).html(" Segment 2: " + value[1].group.cars[1].segment);
})
})
jsfiddle
Since you have the JSON data organised differently from the structure you want, you need to do some pre-processing on it. The easiest way is to linearise it:
function lineariseData(data) {
var ret = [];
$(data.set).each(function() {
var segment = this;
$(segment.segment_summary).each(function() {
var segment_summary = this;
$(segment_summary.children).each(function() {
var section = this;
$(section.children).each(function() {
var measure = this;
ret.push({
segment: segment.segment_desc,
group: segment_summary.section_value,
vertical: section.section_value,
measure: measure.measure,
value: measure.value
});
});
});
});
});
return ret;
}
The above function returns a simple array of objects similar to what you need. After this, since you want to group them by group and vertical, you need to create a hierarchical object:
function groupItems(linearData) {
var groups = [];
var groupNames = [];
$(linearData).each(function() {
if ($.inArray(this.group, groupNames) < 0)
groupNames.push(this.group);
});
var segmentNames = [];
$(linearData).each(function() {
if ($.inArray(this.segment, segmentNames) < 0)
segmentNames.push(this.segment);
});
$(groupNames).each(function() {
var groupName = this;
var itemsInGroup = $(linearData).filter(function() {
return this.group == groupName;
});
var currentGroup = { name: groupName, verticals: [], totalItems: 0 };
groups.push(currentGroup);
var verticalNames = [];
$(itemsInGroup).each(function() {
if ($.inArray(this.vertical, verticalNames) < 0)
verticalNames.push(this.vertical);
});
$(verticalNames).each(function() {
var verticalName = this;
var itemsInVertical = $(itemsInGroup).filter(function() {
return this.vertical == verticalName;
});
var currentVertical = { name: verticalName, measures: [], totalItems: 0 };
currentGroup.verticals.push(currentVertical);
var measureNames = [];
$(itemsInVertical).each(function() {
if ($.inArray(this.measure, measureNames) < 0)
measureNames.push(this.measure);
});
$(measureNames).each(function() {
var measureName = this;
var itemsInMeasure = $(itemsInVertical).filter(function() {
return this.measure == measureName;
});
var currentMeasure = { name: measureName };
currentGroup.totalItems++;
currentVertical.totalItems++;
$(segmentNames).each(function() {
currentMeasure[this] = null;
});
currentVertical.measures.push(currentMeasure);
$(itemsInMeasure).each(function() {
currentMeasure[this.segment] = this.value;
});
});
});
});
return groups;
}
Now you just need to render the data, so if you have a simple table in your markup:
Here is an example for the rendering function:
function renderGroups(groups) {
var table = $('#groupTable').empty();
var header = $("<tr></tr>").appendTo(table);
$('<th>Group</th>').appendTo(header);
$('<th>Vertical</th>').appendTo(header);
$('<th>Measure</th>').appendTo(header);
var headerUpdated = false;
$(groups).each(function() {
var group = this;
var groupCellAdded = false;
$(group.verticals).each(function() {
var vertical = this;
var verticalCellAdded = false;
$(vertical.measures).each(function() {
var measure = this;
var tr = $("<tr />");
if (!groupCellAdded) {
groupCellAdded = true;
$('<td rowspan="' + group.totalItems + '">' + group.name + '</td>').appendTo(tr);
}
if (!verticalCellAdded) {
verticalCellAdded = true;
$('<td rowspan="' + vertical.totalItems + '">' + vertical.name + '</td>').appendTo(tr);
}
$('<td>' + measure.name + '</td>').appendTo(tr);
for (var segment in measure) {
if (segment != 'name') {
if (!headerUpdated)
$('<th>' + segment + '</th>').appendTo(header);
$('<td>' + measure[segment] + '</td>').appendTo(tr);
}
}
headerUpdated = true;
table.append(tr);
});
});
});
}
Now you just need to call those three functions and you are done:
var linearData = lineariseData(data); // data is your JSON object
var groups = groupItems(linearData);
renderGroups(groups);
Here is a jsfiddle to show the whole thing: jsFiddle
Note: your data only has values for group 1 in segment 1 and for group 2 in segment 2, so a few cells will have a null value.
My method is quite similar to Stefano's, but I would rather use a js html template, such as underscore to do this.
I would first create the HTML table
<table id="table">
<thead>
<th>domain group</th>
<th>vertical</th>
<th>measure</th>
<th>Segment 1</th>
<th>Segment 2</th>
</thead>
<tbody>
</tbody>
</table>
then I can use template to fill the data into the table, something like this:
var row = '\
<% for(var i=0;i<children.length;i++) { %>\
<tr>\
<td><%= group %></td>\
<td><%= section_value %></td>\
<td><%= children[i].measure %></td>\
<td><%= children[i].value %></td>\
<td><%= children[i].value %></td>\
</tr>\
<% } %>\
';
items=data.set;
for(var i=0;i<items.length;i++){
var segmentChildren = items[i].segment_summary[0].children;
for(var j=0;j<segmentChildren.length;j++){
var item = segmentChildren[j];
item.group = items[i].segment_summary[0].section_value;
var html = _.template(row,item);
$("#table tbody").append(html);
}
}
After doing some css, you can got all you want. Here's my demo: jsFiddle
I also noticed your missing Segment 1 value in group 2, Segment 2 value in group 1. Your original table simply write the value twice, so I did the same. Moreover, there is no "%" in "% users opening" value, you can either add it during your JSON process, or add it in js.
If you want to get exactly same table in your first format, you can do some html colspan rowspan in the code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With