Looping through a json object in recursive function - javascript

I want to write a recursive function to iterate through a json object using javascript.
I have a sample json file:
{
"week": [
[
{
"id": "121",
"amount": 50,
"numberOfDays": 7,
"data": {
"supply": "xyz",
"price": 50,
}
}
],
[
{
"id": "122",
"amount": 30,
"numberOfDays": 6,
"data": {
"supply": "xyz",
"price": 30,
}
}
],
]
}
I want to take each element of the json object array and pass it to a function.
To extract the array elements I am using this code:
for(var i=0;i<array[plan].length; i++){
var confPlan = array[plan][i];
console.log(plan);
}
var Bill = function (plan) {
return func(plan)
.then((status) => {
if(status == '1') {
// do something
} else if(status == '0') {
Bill(plan) // but with the next element of the array from the json file
}
})
}
please Help!
Thanks in Advance.

It seems your question boils down to wanting to be able to synchronously chain calls to an asynchronous function. I am assuming func is an asynchronous function since your using a .then, so I simulated it with a timeout. Below is one way to achieve the desired behavior recursively:
data = {
"week": [
[{
"id": "121",
"amount": 50,
"numberOfDays": 7,
"data": {
"supply": "xyz",
"price": 50,
}
}],
[{
"id": "122",
"amount": 30,
"numberOfDays": 6,
"data": {
"supply": "xyz",
"price": 30,
}
}],
]
};
function func(data) {
return new Promise((resolve) => {
setTimeout(function() {
console.log(data);
resolve(0);
}, 1000)
})
}
function loop(data, i = 0) {
if (i < data.length) {
func(data[i])
.then((status) => {
if (status) {
// do something
} else {
return loop.call(null, data, i += 1)
}
})
}
}
loop(data['week']);
The ideal solution involves using async/await iteratively in a for loop. Not only will it avoid recursion but the structure of the code is cleaner and more familiar. However, I will leave this to you to research.

Related

Group and count values in an array

I have an array with objects, like the following.
b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
I want to count how many issues have status close, and how many have backlog. I'd like to save the count in a new array as follows.
a = [
{Name: 'Backlog', count: 1},
{Name: 'close', count: 2}
];
I have tried the following.
b.issues.forEach(function(i) {
var statusName = i.fields.status.name;
if (statusName in a.Name) {
a.count = +1;
} else {
a.push({
Name: statusName,
count: 1
});
}
});
That however doesn't seem to be working. How should I implement this?
This is a perfect opportunity to use Array#reduce. That function will take a function that is applied to all elements of the array in order and can be used to accumulate a value. We can use it to accumulate an object with the various counts in it.
To make things easy, we track the counts in an object as simply {name: count, otherName: otherCount}. For every element, we check if we already have an entry for name. If not, create one with count 0. Otherwise, increment the count. After the reduce, we can map the array of keys, stored as keys of the object, to be in the format described in the question. See below.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var counts = b.issues.reduce((p, c) => {
var name = c.fields.status.name;
if (!p.hasOwnProperty(name)) {
p[name] = 0;
}
p[name]++;
return p;
}, {});
console.log(counts);
var countsExtended = Object.keys(counts).map(k => {
return {name: k, count: counts[k]}; });
console.log(countsExtended);
.as-console-wrapper {
max-height: 100% !important;
}
Notes.
Array#reduce does not modify the original array.
You can easily modify the function passed to reduce to for example not distinguish between Backlog and backlog by changing
var name = c.fields.status.name;
into
var name = c.fields.status.name.toLowerCase();
for example. More advanced functionality can also easily be implemented.
Using ES6 Arrow functions you can do it with minimum syntax
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var countOfBackLog = b.issues.filter(x => {
return x.fields.status.name === "Backlog"
}).length
var countOfClose = b.issues.filter(x => {
return x.fields.status.name === "close"
}).length
a =[{Name: 'Backlog', count : countOfBackLog}, {Name: 'close', count : countOfClose}]
More about arrow functions here
You can write like this. It is dynamic.
var a = {};
for(var key in b["issues"]){
if(!a.hasOwnProperty(b["issues"][key].fields.status.name)){
a[b["issues"][key].fields.status.name] = 1;
}else{
a[b["issues"][key].fields.status.name] = a[b["issues"][key].fields.status.name]+1;
}
}
var c = [];
for(var key1 in a){
c.push({
name : key1,
count : a[key1]
});
}
Something like this should do the trick. Simply iterate over your data, keep 2 counters with the number of each type of issue, and create the data format you want in the end. Try it live on jsfiddle.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var data = [];
for(var issue of b.issues){
var entryFound = false;
var tempObj = {
name: issue.fields.status.name,
count: 1
};
for(var item of data){
if(item.name === tempObj.name){
item.count++;
entryFound = true;
break;
}
}
if(!entryFound){
data.push(tempObj);
}
}
console.log(data);

Can i use for loop here instead of writing hundred lines of code.

This is my function. now its suck when there is 200 id.i don't want to write till 200 id's. can i use for loop here ? like this
for(i=0;i<200;i++){
"areas": [ {"id": i}]
}
this is my function
var continentsDataProvider = {
"map": "continentsLow",
"areas": [ {
"id": 1,
}, {
"id": "2",
}, {
}, {
"id": "3",
}, {
}, {
"id": "4",
}, {
}, {
"id": "5",
}, {
} ]
};
Yes you can, simply try
If your object is
var continentsDataProvider = {
"map": "continentsLow",
"areas": []
};
Run your for-loop as
for(var i=1;i<=200;i++){
continentsDataProvider.areas.push( {"id": i});
}
You can use a for loop to create the objects or you can use (the much nicer) Array.from:
var continentsDataProvider = {
"map": "continentsLow",
"areas": Array.from({ length: 200 }, function(k, v) { return { id: v + 1}; })
};
console.log(continentsDataProvider);
As noted by Emil S. Jørgensen in the comments - Array.from is not supported by internet explorer so you'll need to polyfill it (polyfill code).

How to iterate through deeply nested objects inside of a JSON?

I know there are plenty of questions about iterating through JSON objects but I haven't found one that quite relates to my exact problem. This is the JSON that I'm trying to iterate through:
psinsights = {
"kind": "pagespeedonline#result",
"id": "/speed/pagespeed",
"responseCode": 200,
"title": "PageSpeed Home",
"score": 90,
"pageStats": {
"numberResources": 22,
"numberHosts": 7,
"totalRequestBytes": "2761",
"numberStaticResources": 16,
"htmlResponseBytes": "91981",
"cssResponseBytes": "37728",
"imageResponseBytes": "13909",
"javascriptResponseBytes": "247214",
"otherResponseBytes": "8804",
"numberJsResources": 6,
"numberCssResources": 2
},
"formattedResults": {
"locale": "en_US",
"ruleResults": {
"AvoidBadRequests": {
"localizedRuleName": "Avoid bad requests",
"ruleImpact": 0.0
},
"MinifyJavaScript": {
"localizedRuleName": "Minify JavaScript",
"ruleImpact": 0.1417,
"urlBlocks": [
{
"header": {
"format": "Minifying the following JavaScript resources could reduce their size by $1 ($2% reduction).",
"args": [
{
"type": "BYTES",
"value": "1.3KiB"
},
{
"type": "INT_LITERAL",
"value": "0"
}
]
},
"urls": [
{
"result": {
"format": "Minifying $1 could save $2 ($3% reduction).",
"args": [
{
"type": "URL",
"value": "http://code.google.com/js/codesite_tail.pack.04102009.js"
},
{
"type": "BYTES",
"value": "717B"
},
{
"type": "INT_LITERAL",
"value": "1"
}
]
}
},
{
"result": {
"format": "Minifying $1 could save $2 ($3% reduction).",
"args": [
{
"type": "URL",
"value": "http://www.gmodules.com/ig/proxy?url\u003dhttp%3A%2F%2Fjqueryjs.googlecode.com%2Ffiles%2Fjquery-1.2.6.min.js"
},
{
"type": "BYTES",
"value": "258B"
},
{
"type": "INT_LITERAL",
"value": "0"
}
]
}
}
]
}
]
},
"SpriteImages": {
"localizedRuleName": "Combine images into CSS sprites",
"ruleImpact": 0.0
}
}
},
"version": {
"major": 1,
"minor": 11
}
};
Now, I'm trying to write a function that iterates through all of the ruleResults objects and returns an array of the localizedRuleName properties. According to the JSON, ruleResults has three member objects (AvoidBadRequests, MinifyJavaScript, and SpriteImages). Each of these has a localizedRuleName property I'm trying to access, but when I print out my array, it's blank. Here's how I've written my function:
function ruleList(results) {
var ruleArray = [];
for(var ruleName in results.formattedResults.ruleResults){
ruleArray[counter] = results.formattedResults.ruleResults[ruleName].localizedRuleName;
}
return ruleArray;
}
console.log(ruleList(psinsights));
Can you guys help me get on the right track? I used basically this same method to iterate through the pageStats of the JSON and it worked perfectly. I'm not sure why I can't get it to work with these deeper nested objects and properties.
your problem is not your iteration, but your undefined variable "counter".
Instead of using a counter can use the "push" function:
function ruleList(results) {
var ruleArray = [];
for(var ruleName in results.formattedResults.ruleResults){
ruleArray.push(results.formattedResults.ruleResults[ruleName].localizedRuleName);
}
return ruleArray;
}
fiddle: https://jsfiddle.net/fo9h56gh/
Hope this helps.
you're probably getting a javascript error since counter is not defined. you can try this:
function ruleList(results) {
var ruleArray = [];
var counter = 0;
for(var ruleName in results.formattedResults.ruleResults){
ruleArray[counter] = results.formattedResults.ruleResults[ruleName].localizedRuleName;
counter++;
}
return ruleArray;
}

Search deep nested

I am working on a solution where I need to search for an element in a deeply nested JSON by its id. I have been advised to use underscore.js which I am pretty new to.
After reading the documentation http://underscorejs.org/#find , I tried to implement the solution using find, filter and findWhere.
Here is what I tried using find :
var test = {
"menuInputRequestId": 1,
"catalog":[
{
"uid": 1,
"name": "Pizza",
"desc": "Italian cuisine",
"products": [
{
"uid": 3,
"name": "Devilled chicken",
"desc": "chicken pizza",
"prices":[
{
"uid": 7,
"name": "regular",
"price": "$10"
},
{
"uid": 8,
"name": "large",
"price": "$12"
}
]
}
]
},
{
"uid": 2,
"name": "Pasta",
"desc": "Italian cuisine pasta",
"products": [
{
"uid": 4,
"name": "Lasagne",
"desc": "chicken lasage",
"prices":[
{
"uid": 9,
"name": "small",
"price": "$10"
},
{
"uid": 10,
"name": "large",
"price": "$15"
}
]
},
{
"uid": 5,
"name": "Pasta",
"desc": "chicken pasta",
"prices":[
{
"uid": 11,
"name": "small",
"price": "$8"
},
{
"uid": 12,
"name": "large",
"price": "$12"
}
]
}
]
}
]
};
var x = _.find(test, function (item) {
return item.catalog && item.catalog.uid == 1;
});
And a Fiddle http://jsfiddle.net/8hmz0760/
The issue I faced is that these functions check the top level of the structure and not the nested properties thus returning undefined. I tried to use item.catalog && item.catalog.uid == 1; logic as suggested in a similar question Underscore.js - filtering in a nested Json but failed.
How can I find an item by value by searching the whole deeply nested structure?
EDIT:
The following code is the latest i tried. The issue in that is that it directly traverses to prices nested object and tries to find the value. But my requirement is to search for the value in all the layers of the JSON.
var x = _.filter(test, function(evt) {
return _.any(evt.items, function(itm){
return _.any(itm.outcomes, function(prc) {
return prc.uid === 1 ;
});
});
});
Here's a solution which creates an object where the keys are the uids:
var catalogues = test.catalog;
var products = _.flatten(_.pluck(catalogues, 'products'));
var prices = _.flatten(_.pluck(products, 'prices'));
var ids = _.reduce(catalogues.concat(products,prices), function(memo, value){
memo[value.uid] = value;
return memo;
}, {});
var itemWithUid2 = ids[2]
var itemWithUid12 = ids[12]
I dont use underscore.js but you can use this instead
function isArray(what) {
return Object.prototype.toString.call(what) === '[object Array]';
}
function find(json,key,value){
var result = [];
for (var property in json)
{
//console.log(property);
if (json.hasOwnProperty(property)) {
if( property == key && json[property] == value)
{
result.push(json);
}
if( isArray(json[property]))
{
for(var child in json[property])
{
//console.log(json[property][child]);
var res = find(json[property][child],key,value);
if(res.length >= 1 ){
result.push(res);}
}
}
}
}
return result;
}
console.log(find(test,"uid",4));

Transform nested array to D3 multiline time series

I'd like to transform a nested JSON object for a time series D3 multiline chart. I'd like to transform this:
[
{
"utc_date": "2012-12-13T00:00:00.000Z",
"data": {
"view": {
"count": 9061
},
"purchase": {
"count": 254
}
}
},
{
"utc_date": "2012-12-14T00:00:00.000Z",
"data": {
"view": {
"count": 17232
},
"purchase": {
"count": 539
}
}
},
{
"utc_date": "2012-12-15T00:00:00.000Z",
"data": {
"view": {
"count": 28783
},
"purchase": {
"count": 936
}
}
}]
to something like this (or better?)
[
{
"key": "view",
"data": [
{
"date": "2012-12-13T00:00:00.000Z",
"count": 9061
},
{
"date": "2012-12-14T00:00:00.000Z",
"count": 17232
},
{
"date": "2012-12-15T00:00:00.000Z",
"count": 28783
}
]
},
{
"key": "purchase",
"data": [
{
"date": "2012-12-13T00:00:00.000Z",
"count": 254
},
{
"date": "2012-12-14T00:00:00.000Z",
"count": 539
},
{
"date": "2012-12-15T00:00:00.000Z",
"count": 936
}
]
}
]
To be clear, "view" and "purchase" will not be known and are dynamic. So they shouldn't be hardcoded into the example.
But still wondering if there is any D3.js method or chain of methods to transform this data?
Assuming your original array is data, you could do something like this:
V=data.map(function(d){
w=d.data;
M=Array();
for (e in w) { // to loop for all parameters
M.push({"date":d.utc_date, "param":e,"count":w.view.count})
}
return M;
})
V=d3.merge(V)
V=d3.nest().key(function(d){return d.param}).entries(V);
V will hold your new data structure.
I am sure that this can be further improved.
EDIT
Perhaps you don't even need the d3.merge if you only append to another Array to begin with.
M=Array();
V=data.map(function(d){
w=d.data;
for (e in w) {
M.push({"date":d.utc_date, "param":e,"count":w.view.count})
}
})
V=d3.nest().key(function(d){return d.param}).entries(M);
I will let you experiement and see the difference in the performance and code structure.
Hope it helps
Just improving #Nikos answer below to cater for dynamic keys and, although it doesn't use a D3 method chain, this worked:
var result = [];
data.forEach(function(d) {
w = d.data;
for (var k in w) {
if (w.hasOwnProperty(k)) {
result.push({
"date":d.utc_date,
"type":k,
"count":w[k].count
});
}
}
});
JSFiddle here: http://jsfiddle.net/3umm391n/8/

Categories

Resources