How to parse nested JSON in Javascript? - javascript

I am trying to parse and show JSON data (product catalog) using XMLHttpRequest method. I am able to display the brands and their names, but not able to showcase list of products progmatically.
Here is the sample JSON request:
{
"products": {
"laptop": [{
"brand": "sony",
"price": "$1000"
}, {
"brand": "acer",
"price": "$400"
}],
"cellphone": [{
"brand": "iphone",
"price": "$800"
}, {
"brand": "htc",
"price": "$500"
}],
"tablets": [{
"brand": "iPad",
"price": "$800"
}, {
"brand": "htc-tab",
"price": "$500"
}]
}
}
Right now I am using following code to show data in tabluar form:
function loadJSON() {
var data_file = "http://localhost/AJAX/productcatalog.json";
var http_request = new XMLHttpRequest();
http_request.onreadystatechange = function () {
if ((http_request.readyState == 4) && (http_request.status == 200)) {
// Javascript function JSON.parse to parse JSON data
var jsonObj = JSON.parse(http_request.responseText);
data = '<table border="2"><tr><td>Type</td><td>Brand</td><td>Price</td></tr>';
var i = 0;
debugger;
for (i = 0; i < jsonObj["products"].laptop.length; i++)
{
obj = jsonObj["products"].laptop[i];
data = data + '<tr><td>laptop</td><td>' + obj.brand + '</td><td>' + obj.price + '</td></tr>';
}
for (i = 0; i < jsonObj["products"].cellphone.length; i++)
{
obj = jsonObj["products"].cellphone[i];
data = data + '<tr><td>laptop</td><td>' + obj.brand + '</td><td>' + obj.price + '</td></tr>';
}
for (i = 0; i < jsonObj["products"].tablets.length; i++)
{
obj = jsonObj["products"].tablets[i];
data = data + '<tr><td>laptop</td><td>' + obj.brand + '</td><td>' + obj.price + '</td></tr>';
}
data += '</table>';
document.getElementById("demo").innerHTML = data;
}
}
http_request.open("GET", data_file, true);
http_request.send();
}
Question What is the way to fetch product list , i.e. products, cellphone and tablets ? Right now I have hardcoded that in order to fetch complete list of brands. Please advice. (I want to use plain javascript and not jquery)
Thanks!

It sounds like what you're missing is the "How do I iterate over an object when I don't know all the keys".
An object is a set of key, value pairs. You can use for/in syntax: for( var <key> in <object> ){} to get each key.
For your use case it might be something like:
var products = jsonObject['products'];
for( var productName in products ){
//productName would be "laptop", "cellphone", etc.
//products[productName] would be an array of brand/price objects
var product = products[productName];
for( var i=0; i<product.length; i++ ){
//product[i].brand
//product[i].price
}
}
In practice, I might use something a little less verbose, but this makes it easier to understand what is going on.

To achieve the expected i have used for loop and HTML DOM createElement() Method
var product_catalog = {
"products": {
"laptop": [{
"brand": "sony",
"price": "$1000"
}, {
"brand": "acer",
"price": "$400"
}],
"cellphone": [{
"brand": "iphone",
"price": "$800"
}, {
"brand": "htc",
"price": "$500"
}],
"tablets": [{
"brand": "iPad",
"price": "$800"
}, {
"brand": "htc-tab",
"price": "$500"
}]
}
};
var output = document.querySelector('#product tbody');
function build(JSONObject) {
/**get all keys***/
var keys = Object.keys(JSONObject);
/**get all subkeys***/
var subkeys = Object.keys(JSONObject[keys]);
console.log(subkeys);
/**loop sub keys to build HTML***/
for (var i = 0, tr, td; i < subkeys.length; i++) {
tr = document.createElement('tr');
td = document.createElement('td');
td.appendChild(document.createTextNode(subkeys[i]));
tr.appendChild(td);
output.appendChild(tr);
}
};
build(product_catalog);
HTML:
Coepen URL for reference- http://codepen.io/nagasai/pen/xOOqMv
Hope this works for you :)

Look at this example:
var x = data.key1.children.key4;
var path = "data";
function search(path, obj, target) {
for (var k in obj) {
if (obj.hasOwnProperty(k))
if (obj[k] === target)
return path + "['" + k + "']"
else if (typeof obj[k] === "object") {
var result = search(path + "['" + k + "']", obj[k], target);
if (result)
return result;
}
}
return false;
}
//Then for evry node that you need you can call the search() function.
var path = search(path, data, x);
console.log(path); //data['key1']['children']['key4']

I think this is what you're asking about, you can use Object.keys to get the properties of an object, then loop through them afterward.
var data = {
"products": {
"laptop": [{
"brand": "sony",
"price": "$1000"
}, {
"brand": "acer",
"price": "$400"
}],
"cellphone": [{
"brand": "iphone",
"price": "$800"
}, {
"brand": "htc",
"price": "$500"
}],
"tablets": [{
"brand": "iPad",
"price": "$800"
}, {
"brand": "htc-tab",
"price": "$500"
}]
}
}
var typesOfProducts = Object.keys(data.products)
console.log(typesOfProducts)
document.getElementById('output').textContent = typesOfProducts.toString()
//Then, to loop through
var i = -1,
len = typesOfProducts.length
function handleProduct(productType) {
console.log("This is the " + productType + " data.")
console.log(data.products[productType])
}
while (++i < len) {
handleProduct(typesOfProducts[i])
}
<div id="output"></div>

It sounds like what you're looking for is just an array of the keys of the "products" object. Example:
Products: ["laptop", "cellphone", "tablets"];
If so, I would just run your json object through javascript's Object.keys() method.
var jsonObj = JSON.parse(http_request.responseText);
var products = Object.keys(jsonObj.products);
// products = ["laptop", "cellphone", "tablets"];

Related

Json - create an ID column that auto increment

I'm building a json object from 2 dataset and I need to add a column (ID) with a unique value,
I have thought that having an auto increment ID (1,2,3,4..) value will be great
This is how I'm building my Json object with JavaScript
var output = [];
for (var rowIdx = 0; rowIdx < csv.length; rowIdx++) {
var row = {};
for (var fieldIdx =0; fieldIdx < fields.length; fieldIdx++) {
var field = editor.field( fields[fieldIdx] );
var mapped = data[ field.name() ];
row[field.name()] = csv[rowIdx][mapped];
}
output.push(row);
}
var json = JSON.stringify(output);
console.log(json)
and this is my JSON: PLEASE NOTE: I want to add "id": "1" column whose value will auto increment for each record
[
{
"id": "1", <--- DESIRED
"title": "Hello World",
"artist": "John Smith",
"genre": "pop",
"week": "4",
"highest_rating": "3",
"year": "2014",
"youtube": "www"
]
},
{
"id": "2", <--- DESIRED
"title": "Lorem Ipsum",
"artist": "John Smith",
"genre": "pop",
"week": "4",
"highest_rating": "3",
"year": "2014",
"youtube": "www"
]
}
]
you can do this row.id = (rowIdx + 1);
var output = [];
for (var rowIdx = 0; rowIdx < csv.length; rowIdx++) {
var row = {};
for (var fieldIdx = 0; fieldIdx < fields.length; fieldIdx++) {
var field = editor.field(fields[fieldIdx]);
var mapped = data[field.name()];
row[field.name()] = csv[rowIdx][mapped];
}
row.id = (rowIdx + 1); // <--- this line
output.push(row);
}
var json = JSON.stringify(output);
console.log(json);
You can increment an i number starting from 0, for Id of each object in the output array. For example:
for(let i=0; i<output.length; i++)
{ output[i].id=i+1;}

How can one iterate with this JSON response file

As said above, I don't know how to use this kind of JSON response from my server-side php which I got by using this code echo json_encode(array_merge($outp, $outp2));
[
{
"stuid":"12",
"stuname":"Velino Meratis",
"stucourse":"BSIT",
"stustat":"0",
"stulyear":"4",
"stulog":"feb 16 2017"
},
{
"stuid":"13",
"stuname":"Alana Melker",
"stucourse":"BSCE",
"stustat":"1",
"stulyear":"5",
"stulog":"feb 16 2017"
},
{
"stuid":"12",
"cname":"InfoTech000",
"clog":"1"
},
{
"stuid":"12",
"cname":"InfoTech001",
"clog":"2"
},
{
"stuid":"12",
"cname":"C101",
"clog":"3"
},
{
"stuid":"13",
"cname":"CE000",
"clog":"4"
},
{
"stuid":"13",
"cname":"CE001",
"clog":"5"
},
{
"stuid":"13",
"cname":"C101",
"clog":"6"
}
]
If I use this code in my client side javascript
if (this.readyState == 4 && this.status == 200) {
students = JSON.parse(this.responseText);
students.forEach(function(item){
console.log(item.stuid);
x = item.stuid;
document.getElementById("demo").innerHTML += x + " " + item.stuname + "<br>" + item.cname + "<br>";
});
}
it just ends up giving me this:-
12 Velino Meratis
undefined
13 Alana Melker
undefined
Somehow I can iterate the stuid and the stunamebut it won't allow them to contain the cname as an array with them.
How can I turn that into something like this:-
12 Velino Meratis
InfoTech000, InfoTech001, C101
13 Alana Melker
CE000, CE001, C101
Can someone Help and Elaborate on this?
You can check if the array contains the key or not, that way you will be saved from "undefined" error.
You can do it this way
if(item.hasOwnProperty('cname'))
{
console.log(item.cname);
}
You can use this for stuname or other keys also.
You need to merge student objects in your array by unique IDs. One way to do this is to add new stuids to an array and merge it with subsequent items with same stuid. Once you have array of unique students, you can proceed with other goals.
if (this.readyState == 4 && this.status == 200) {
var students_raw = JSON.parse(this.responseText);
var students = [];
students_raw.forEach(function(item){
var existing = students.find($item => item.stuid === $item.stuid);
if (existing) {
existing = Object.assign({}, existing, item);
} else {
students.push(item);
}
});
// your print loop
students.forEach(function(item) {
var x = item.stuid;
document.getElementById("demo").innerHTML += x + " " + item.stuname + "<br>" + item.cname + "<br>";
});
}
Please note: Array.reduce() and Object.assign() are not supported widely. you may need to polyfill these methods
NOTE: This answer assumes you're willing and able to change the data coming from PHP
Credit where credit is due, this is an extension of #Magnus Eriksson's comment.
It would be preferable to keep the relevant data associated with each other. You should get better flexibility with well-formed data. Ideally, you should do this server side and present the data to the client already well formatted.
You should try and achieve something similar to the following for your output:
[{
"stuid": "12",
"stuname": "Velino Meratis",
"stucourse": "BSIT",
"stustat": "0",
"stulyear": "4",
"stulog": "feb 16 2017",
"classes": [{
"cname": "InfoTech000",
"clog": "1"
}, {
"cname": "InfoTech001",
"clog": "2"
}, {
"cname": "C101",
"clog": "3"
}]
}, {
"stuid": "13",
"stuname": "Alana Melker",
"stucourse": "BSCE",
"stustat": "1",
"stulyear": "5",
"stulog": "feb 16 2017",
"classes": [{
"cname": "CE000",
"clog": "4"
}, {
"cname": "CE001",
"clog": "5"
}, {
"cname": "C101",
"clog": "6"
}]
}];
Note I've used classes as the property name as it appears we are working with Courses and their classes.
Your javascript will now look like:
if (this.readyState == 4 && this.status == 200) {
students = JSON.parse(this.responseText);
students.forEach(function(item){
console.log(item.stuid);
x = item.stuid;
document.getElementById("demo").innerHTML += x + " " + item.stuname + "<br>" + item.classes.map(function(elem){
return elem.cname;}).join(",") + "<br>";
});
}
Note the map function won't work in IE8 and lower.
Now for a working example:
var students = [{
"stuid": "12",
"stuname": "Velino Meratis",
"stucourse": "BSIT",
"stustat": "0",
"stulyear": "4",
"stulog": "feb 16 2017",
"classes": [{
"cname": "InfoTech000",
"clog": "1"
}, {
"cname": "InfoTech001",
"clog": "2"
}, {
"cname": "C101",
"clog": "3"
}]
}, {
"stuid": "13",
"stuname": "Alana Melker",
"stucourse": "BSCE",
"stustat": "1",
"stulyear": "5",
"stulog": "feb 16 2017",
"classes": [{
"cname": "CE000",
"clog": "4"
}, {
"cname": "CE001",
"clog": "5"
}, {
"cname": "C101",
"clog": "6"
}]
}];
students.forEach(function(item){
console.log(item.stuid);
x = item.stuid;
document.getElementById("demo").innerHTML += x + " " + item.stuname + "<br>" + item.classes.map(function(elem){
return elem.cname;}).join(",") + "<br>";
});
<div id="demo"></div>
As mentioned in other answers and comments, the issue with your existing code, is not all objects in your json array have the property you're referencing.
You can try like this once also
HTML
<div id="result"></div>
SCRIPT
var dataJSON = [
{
"stuid":"12",
"stuname":"Velino Meratis",
"stucourse":"BSIT",
"stustat":"0",
"stulyear":"4",
"stulog":"feb 16 2017"
},
{
"stuid":"13",
"stuname":"Alana Melker",
"stucourse":"BSCE",
"stustat":"1",
"stulyear":"5",
"stulog":"feb 16 2017"
},
{
"stuid":"12",
"cname":"InfoTech000",
"clog":"1"
},
{
"stuid":"12",
"cname":"InfoTech001",
"clog":"2"
},
{
"stuid":"12",
"cname":"C101",
"clog":"3"
},
{
"stuid":"13",
"cname":"CE000",
"clog":"4"
},
{
"stuid":"13",
"cname":"CE001",
"clog":"5"
},
{
"stuid":"13",
"cname":"C101",
"clog":"6"
}
] ;
var arr = {};
for(var i = 0 ; i< dataJSON.length ; i++){
var ele = dataJSON[i];
if(arr[ ele.stuid ]==undefined){
arr[ ele.stuid ] = {};
}
arr[ ele.stuid ]['stuid'] = ele.stuid;
if(ele.stuname!=undefined){
arr[ ele.stuid ]['stuname'] = ele.stuname;
}
if(arr[ ele.stuid ]['cname'] == undefined){
arr[ ele.stuid ]['cname'] = [];
}
if(ele.cname!=undefined){
arr[ ele.stuid ]['cname'].push(ele.cname);
}
}
var str = '';
for(var key in arr){
var obj = arr[key];
str += obj['stuid'] +" "+obj['stuname']+'<br/>';
str += obj['cname'].toString()+'<br/>';
}
document.getElementById("result").innerHTML = str;
Thanks,

Filtering array with specific values

I have this JSON:
{
"1": {
"PerId":"10900662",
"Name":"Cueball",
"Email":"cueb#example.com",
"DepartId":"11"
},
"2": {
"PerId":"10900664",
"Name":"Megan",
"MuEmail":"megan#example.com",
"DepartId":"11"
},
"3": {
"PerId":"10900665",
"Name":"Beret Guy",
"MuEmail":"bg#example.com",
"DepartId":"12"
}
}
Which I want to filter with a specific DepartId
Here's the code I've tested, which is from this Stack Overflow question:
<html>
<body>
Test!
</body>
<script>
var y = JSON.parse('{"1":{"PerId":"10900662","Name":"Cueball","Email":"cueb#example.com","DepartId":"11"},"2": {"PerId":"10900664","Name":"Megan","MuEmail":"megan#example.com","DepartId":"11"},"3": {"PerId":"10900665","Name":"Beret Guy","MuEmail":"bg#example.com","DepartId":"12"}}');
var z = y.filter(function (i,n){return n.DepartId == '11'})
</script>
</html>
In this case, y returns an object, but Firefox throws error that y.filter is not a function.
I expected y to return as something like
{
"1": {
"PerId":"10900662",
"Name":"Cueball",
"Email":"cueb#example.com",
"DepartId":"11"
},
"2": {
"PerId":"10900664",
"Name":"Megan",
"MuEmail":"megan#example.com",
"DepartId":"11"
}
}
in the form of JavaScript object. How do I make it work?
filter() is defined on the Array prototype. It cannot be used on object.
You can use Object.keys() to get all the keys in the object and for loop to iterate over them.
// Get all keys from the `obj`
var keys = Object.keys(obj),
result = {};
// Iterate over all properties in obj
for (var i = 0, len = keys.length; i < len; i++) {
// If the ID is to be filtered
if (obj[keys[i]].DepartId === '11') {
// Add the object in the result object
result[keys[i]] = obj[keys[i]];
}
}
var obj = {
"1": {
"PerId": "10900662",
"Name": "Cueball",
"Email": "cueb#example.com",
"DepartId": "11"
},
"2": {
"PerId": "10900664",
"Name": "Megan",
"MuEmail": "megan#example.com",
"DepartId": "11"
},
"3": {
"PerId": "10900665",
"Name": "Beret Guy",
"MuEmail": "bg#example.com",
"DepartId": "12"
}
};
var keys = Object.keys(obj),
result = {};
for (var i = 0, len = keys.length; i < len; i++) {
if (obj[keys[i]].DepartId === '11') {
result[keys[i]] = obj[keys[i]];
}
}
console.log(result);
document.body.innerHTML = '<pre>' + JSON.stringify(result, 0, 4);
I'll recommend to change the data format to use array of objects. Then filter() can be directly applied on array.
var arr = [{
"PerId": "10900662",
"Name": "Cueball",
"Email": "cueb#example.com",
"DepartId": "11"
}, {
"PerId": "10900664",
"Name": "Megan",
"MuEmail": "megan#example.com",
"DepartId": "11"
}, {
"PerId": "10900665",
"Name": "Beret Guy",
"MuEmail": "bg#example.com",
"DepartId": "12"
}];
var result = arr.filter(obj => obj.DepartId === '11');
console.log(result);
var arr = [{
"PerId": "10900662",
"Name": "Cueball",
"Email": "cueb#example.com",
"DepartId": "11"
}, {
"PerId": "10900664",
"Name": "Megan",
"MuEmail": "megan#example.com",
"DepartId": "11"
}, {
"PerId": "10900665",
"Name": "Beret Guy",
"MuEmail": "bg#example.com",
"DepartId": "12"
}];
var result = arr.filter(obj => obj.DepartId === '11');
console.log(result);
document.body.innerHTML = '<pre>' + JSON.stringify(result, 0, 4);
You're trying to use an array filter on an object. Look over the example again.
$([ // this is an array of objects
{"name":"Lenovo Thinkpad 41A4298","website":"google222"},
{"name":"Lenovo Thinkpad 41A2222","website":"google"}
])
.filter(function (i,n){
return n.website==='google';
});
filter is prototype of array function not object,so we can not use here.
To achieve output, we can use for-in loop on object
var y = JSON.parse('{"1":{"PerId":"10900662","Name":"Cueball","Email":"cueb#example.com","DepartId":"11"},"2": {"PerId":"10900664","Name":"Megan","MuEmail":"megan#example.com","DepartId":"11"},"3": {"PerId":"10900665","Name":"Beret Guy","MuEmail":"bg#example.com","DepartId":"12"}}');
var res = {}
for(var key in y){
if(y[key].DepartId === '11')
res[key] = y[key]
}
console.log(res)

Push Unique Objects to JavaScript Array

How do I push an object into an specified array that only updates that array? My code pushes an object and updates all arrays, not just the specified one.
Here is the structure of the data:
{
"d": {
"results": [
{
"Id": 1,
"cost": "3",
"item": "Project 1",
"fiscalyear": "2014",
"reportmonth": "July"
}
]
}
}
Here is a sample of the desired, wanted results:
{
"Project 1": [
{
"date": "31-Jul-14",
"rating": "3"
},
{
"date": "31-Aug-14",
"rating": "4"
}
],
"Project 2": [
{
"date": "31-Jul-14",
"rating": "2"
}
]
}
This is my attempt:
var results = data.d.results;
var date;
var projectObj = {},
projectValues = {},
project = '';
var cost = '',
costStatus = '';
for (var i = 0, m = results.length; i < m; ++i) {
project = results[i]['item'];
if (!projectObj.hasOwnProperty(project)) {
projectObj[project] = [];
}
// use Moment to get and format date
date = moment(new Date(results[i]['reportmonth'] + ' 1,' + results[i]['fiscalyear'])).endOf('month').format('DD-MMM-YYYY');
// get cost for each unique project
costStatus = results[i]['cost'];
if (costStatus == null || costStatus == 'N/A') {
cost = 'N/A';
}
else {
cost = costStatus;
}
projectValues['rating'] = cost;
projectValues['date'] = date;
projectObj[project].push(projectValues);
}
Here is a Fiddle with the undesired, unwanted results:
https://jsfiddle.net/yh2134jn/4/
What am I doing wrong?
That is because You do not empty it new iteration. Try this:
for (var i = 0, m = results.length; i < m; ++i) {
projectValues = {};
project = results[i]['item'];
....
}

Show value of an array from json object once when is duplicate

I'm trying to form a list from json data, see the example What I want is that it will show me the value just once when duplicate values occurs (2x Martini glass, I want it to return just one in the list), but leaves the array as is, i.e. still want to be able to hold all values in the array.
There'd sure be a simple way to achieve this, but i'm not finding it...
var data = {
"cocktails": [{
"name": "Bloody Mary",
"glass": "longdrink",
"ingredients": {
"main": "vodka",
"secondary": "tomato juice",
"addition": "tabasco"
}
}, {
"name": "Daiquiri",
"glass": "martini glass",
"ingredients": {
"main": "white rum",
"secondary": "lime juice",
"addition": "sugar syrup"
}
}, {
"name": "Martini",
"glass": "martini glass",
"ingredients": {
"main": "gin",
"secondary": "vermout",
"addition": "olive"
}
}]
}
$(data.cocktails).each(function () {
var output = "<ul><li>" + this.glass + "</li></ul>";
$('#placeholder').append(output);
});
Create an empty array:
var glasses = [];
Push all the glasses to it.
data.cocktails.forEach(function (el) {
glasses.push(el.glass);
});
Create a deduped array of glasses. This leaves the glasses array intact so you can use it again.
var dedupedGlasses = glasses.filter(function(elem, pos) {
return glasses.indexOf(elem) == pos;
});
Use join to create the HTML list.
var html = '<ul><li>' + dedupedGlasses.join('</li><li>') + '</li></ul>';
And add to the page.
$('#placeholder').append(html);
Demo
The you can use something like this to grab the count for each glasses type:
function getCount(arr) {
return arr.reduce(function(m, e){
m[e] = (+m[e]||0)+1; return m
}, {});
}
console.log(getCount(glasses)); // { longdrink=1, martini glass=2 }
Basically the same as #Andy, though slightly different.
var data = {
"cocktails": [{
"name": "Bloody Mary",
"glass": "longdrink",
"ingredients": {
"main": "vodka",
"secondary": "tomato juice",
"addition": "tabasco"
}
}, {
"name": "Daiquiri",
"glass": "martini glass",
"ingredients": {
"main": "white rum",
"secondary": "lime juice",
"addition": "sugar syrup"
}
}, {
"name": "Martini",
"glass": "martini glass",
"ingredients": {
"main": "gin",
"secondary": "vermout",
"addition": "olive"
}
}]
}
$('#placeholder').append('<ul><li>' + data.cocktails.map(function (cocktail) {
return cocktail.glass;
}).filter(function (glass, index, array) {
return array.indexOf(glass) === index;
}).join('</li><li>') + '</li></ul>');
On jsFiddle
I can just point to a solution where you need to define a new array
var cleanArr = []
$(data.cocktails).each(function () {
if($.inArray(this.glass, cleanArr) === -1) cleanArr.push(this.glass);
var output = "<ul><li>" + this.glass + "</li></ul>";
$('#placeholder').append(output);
});
console.log(cleanArr)
I like the way the array is created, that's why I'm posting this solution but it is not my idea --> taken from:
Remove Duplicates from JavaScript Array
Here is a working jsFiddle for you.
http://jsfiddle.net/Q74pj/1/
And here is what I did:
I used a new function calling uniqueArray to create a new associative array (which would be the quickest to iterate and fill).
You need to create a unique array, or sort it by drink and skip the duplicates.
function uniqueValues (arr) {
//if (arr.length < 2) return arr;
var retVal = {};
for (var i = 0; i < arr.length; i++) {
retVal[arr[i].glass] = arr[i];
}
return retVal;
}
then just use another way to iterate the associate array:
var uniqueArrayValues = uniqueValues(data.cocktails);
for (key in uniqueArrayValues) {
var output = "<ul><li>" + uniqueArrayValues[key].glass + "</li></ul>";
$('#placeholder').append(output);
}

Categories