
Recently I had a requirement to take a list of JSON objects that had a hierarchical structure and represent it as a table. For reference, here’s a sample of what the JSON object looked something like. If you want to try on your own, feel free to copy below.
[{ "ProductVariants": [{ "Images": [{ "ImageTypeID": 27, "ImageType": "Hi-Res", "FileAndPath": "http://path/to/hires-image.png", "AlternateText": "", "LastUpdated": "2016-06-07T15:38:02.437" }, { "ImageTypeID": 25, "ImageType": "Preview", "FileAndPath": "http://path/to/prev-image.png", "AlternateText": "", "LastUpdated": "2016-06-07T15:38:02.497" }], "SKU": "1234", "UPC": "0-00000-01234-0", "Status": "Active", "Flavour": "Grapefruit" }], "Documents": [{ "DocumentID": 22059, "Title": "Liquid-3Grapefruit", "DocumentTypeID": 278, "FileandPath": "http://path/to/doc-22059.pdf", "LastUpdated": "2016-01-06T13:48:27.073" }], "Attributes": [{ "Heading": "Positioning line", "Description": "A great source of deliciousness" }, { "Heading": "Preview summary", "Description": "Lorem Ipsum dolor Lorem Ipsum dolor Lorem Ipsum dolor Lorem Ipsum dolor " }], "ID": 4432, "PrimaryName": "Super Mega Grapefruit Chewies", "Form": "Liquid", "Flavour": "Grapefruit", "MainImage": "http://path/to/image.png" }]
This represented one product, but the actual dataset had many more fields, and hundreds of products. As you can see, there are arrays and arrays of arrays. What I was trying to do was to parse the object and represent each product as a table. I would show each key, and then beside it, I would show the value. I didn’t care about the hierarchical nature of the data, I wanted to flatten it into a single table so it could be moved to something like Excel and worked on without too much trouble.
Getting started: Output the attributes
I knew that there’s likely be some recursion that I needed to do, but for starters, I just wanted to build the framework.
So, after I grabbed the JSON data from the API and put it into a variable called products
, I set out just to process the basic elements. For this, I could use a basic for
loop, but I decided to use jQuery’s $.each()
which will take care of the browser inconsistencies. You could use Plain vanilla javascript and there’s a great article on Medium on how to convert $.each
to vanilla JS. I recommend you check that out later. jQuery can become a crutch and sometimes it’s just not needed.
// iterate through all products ( we have only 1 in our data ) $.each(products, function(){ // iterate through the properties $.each(this, function(index,value){ console.log(index + ' : ' + value); }) });
Basically all we’re doing here is iterating through the products and then iterating through each property on the root level. As we are just logging to the console, once you open up web inspector, you’ll see the following in the output if you use the sample data:
ProductVariants : [object Object] Documents : [object Object] Attributes : [object Object],[object Object] ID : 4432 PrimaryName : Super Mega Grapefruit Chewies Form : Liquid Flavour : Grapefruit MainImage : http://path/to/image.png
Flatten the Hierarchy! Down With Structure!
So far, so good, but we haven’t flattened anything yet. We want all our data on the same level and we don’t care about where they are on the tree. Right now, [object Object]
isn’t all that useful. We want the data inside those objects. Before we go any further, let’s refactor our code to take the view layer ( currently just outputting to the console ) and put it in it’s own function called: makeRow
.
function makeRow(item){ $.each(item, function(title,data){ console.log(title + " : " + data); }) } // iterate through all products ( we have 1 in our data ) $.each(products, function(){ makeRow(this); });
We haven’t changed anything, we just shifted things around, but this step will be important as we want to start going deeper down the tree. There are a few ways to do it, including manually calling the function for each property that we know is an object or an array, but rather than go down this path, we’re going to combine the typeof
operator and… recursion.
Recursion, Really?
Recursion seems like a complicated scary thing, until you realize that essentially something we deal with quite frequently. It exists quite frequently in nature ( tree branches, for example ) and we use a form of it when travelling places. We may want to travel across the country in a car. That route ( west to east, for example ) has roads within it. Maybe there are 4 different highway you can take. Within each of those roads there are roads, perhaps an express road in the center. Within this express road there may be two lanes. This is the smallest unit that you can travel on by car. A lane could be seen as a road within a road within a road. Recursion, from a computer’s point of view is simply making a decision at each junction and deciding whether we want to go deeper or if we’re at the core yet.
Our structure is the perfect use case for recursion. It allows the computer do all the grunt work of figuring stuff out. We’re just going to make a simple change to our makeRow
function:
function makeRow(item){ $.each(item, function(title,data){ if( typeof data === 'object' ) showRow(data); else console.log(title + " : " + data); }); }
And voila, we now have our complete dataset, flattened:
ImageTypeID : 27 ImageType : Hi-Res FileAndPath : http://path/to/hires-image.png AlternateText : LastUpdated : 2016-06-07T15:38:02.437 ImageTypeID : 25 ImageType : Preview FileAndPath : http://path/to/prev-image.png AlternateText : LastUpdated : 2016-06-07T15:38:02.497 SKU : 1234 UPC : 0-00000-01234-0 Status : Active Flavour : Grapefruit DocumentID : 22059 Title : Liquid-3Grapefruit DocumentTypeID : 278 FileandPath : http://path/to/doc-22059.pdf LastUpdated : 2016-01-06T13:48:27.073 Heading : Positioning line Description : A great source of deliciousness Heading : Preview summary Description : Lorem Ipsum dolor Lorem Ipsum dolor Lorem Ipsum dolor Lor... ID : 4432 PrimaryName : Super Mega Grapefruit Chewies Form : Liquid Flavour : Grapefruit MainImage : http://path/to/image.png
Finishing up with some tabular touches
Now, it’s just a matter of making a few changes to our makeRow
function to build up some table rows and add them to an element on the page:
function makeRow(item){ var rows = ''; $.each(item, function(title,data){ if( typeof data === 'object' ) { rows += '<tr><th>' + title + '</th><td>↓</td></tr>'; rows += makeRow(data); } else { rows += '<tr><td>' + title + "</td><td>" + data + '</td></tr>'; } }); return rows; } // iterate through all products ( we have 1 in our data ) var table = ''; $.each(products, function(){ table = '<table><tbody>'; table += makeRow(this); table += '</tbody></table>'; }); $('#tbl').html(table);
Of course, you can get a lot fancier than this and check for arrays and treat them differently than objects, but for my purposes, this was all I needed. In a future article, I will go into how you can take a list of items ( like products ) and transpose this data horizontally. It’s a little but trickier to do this.