Using nodejs to parse JSON - javascript

So, I'm using nodejs to parse json, and I'm wanting to get an object that's an array and use the values in a for each loop.
JSON I'm getting: http://tmi.twitch.tv/group/user/loneztar/chatters
var cronUsers = client.utils.cronjobs('1 * * * * *', function() {
console.log('API Test');
console.log('Response from Twitch Chat:');
request('http://tmi.twitch.tv/group/user/loneztar/chatters', function (error, response, body) {
if (!error && response.statusCode == 200) {
data = JSON.parse(body);
console.log(data.chatters.viewers);
//for each string in the viewers
}
})
});
Above is the code I'm using to retrieve the data. And when I log to the console I get the following (at time of asking):
[ 'duckziller72',
'adanaran',
'nyckeln',
'diabolicalsheep69',
'audery101',
'gone_nutty',
'k4unl',
'eidokan',
'mattesolo',
'siland',
'nullvoid8',
'troy00114',
'sixdev',
'jake_evans',
'doctoranguus',
'juicegraip',
'k4rush' ]
Now I want to use each of them strings in a for each loop. I've tried using the data var I'm using but I can't get anything working. Would appreciate some help! Feel free to ask for other stuff, I'll happily edit.

There are a few different ways to do this.
Using a loop counter:
var viewers = data.chatters.viewers;
var numViewers = viewers.length;
for (var i = 0; i < numViewers; i++) {
var viewer = viewers[i];
console.log(viewer);
}
Using 'for .. in':
var viewers = data.chatters.viewers;
for (var i in viewers) {
var viewer = viewers[i];
console.log(viewer);
}
You may have been trying to use this, but the loop variable contains the indices of the array, not the values.
Using Array.prototype.forEach():
data.chatters.viewers.forEach(function(viewer) {
console.log(viewer);
});
Note that none of this is specific to Node.js or JSON responses.

Related

Getting a value from JSON API

So as of now I have successfully pulled data from a 3rd party website's API, but I'm still struggling to pull the value out of it. The data is formed in the following way:
My code currently looks somewhat like this:
Request.get("url", (error, response, body) => {
if(error) {
return console.dir(error);
}
console.dir(JSON.parse(body));
var obj = JSON.parse(body);
var priice = obj.data.items_on_sale.price;
console.log(priice);
});
So I'd like to pull out the price value but I don't know how to do it correctly. If I remove the last 3 lines I get this: "data: { items_on_sale: [ [Object] ], items_not_on_sale: [] } }", and if I keep it as it is I still can't pull the price value successfully... Any advice?
var obj = JSON.parse(body);
var priice = obj.data.items_on_sale[0].price;
console.log(priice);
This is what you are looking for.
var obj = JSON.parse(body);
var priice = obj.data.items_on_sale[0].price;
console.log(priice);
Is the way to go :)

How to use multiple callbacks when looping data

I'm trying to get HTML form data, loop it through, change it a bit and insert it to database. I have tried like below app.js.
How can I make callbacks so that formdata what I have modified is available for .create function?
I have searched from everywhere and I always end up in dead end and undefined variable somehow.
app.js:
//Find the day where to save
Day.findById(req.params.id, function(err, day) {
if (err) {
console.log(err);
res.redirect("/diary");
} else {
// Search function to find data with _id
function ingredientIdQuery(reqBodyId) {
var ingQuery = Ingredient.find({_id:reqBodyId});
return dbQuery;
}
// This loops through HTML formdata and formats it for mongoose model
for (var i = 0; i < req.body.amount.length; i++) {
if (req.body.amount[i] !== "") {
var amount = Number(req.body.amount[i]);
var singleMealTempObj = {};
singleMealTempObj.amount = amount;
var _id = req.body.id[i];
var query = ingredientIdQuery(_id);
// Executing the query for the data I need with id
query.exec(function(err, ingr){
if(err) {
return console.log(err);
} else {
singleMealTempObj.ingredient = ingr[0];
singleMealTempArr.push(singleMealTempObj);
}
});
}
}
}
// This inserts data into day
Meal.create(singleMealTempArr, function(err, singleMealObject) {
if (err) {
console.log(err);
} else {
day.meals.push(singleMealObject);
day.save();
res.redirect("/day/" + day._id + "/dayshow");
}
});
});
});
Edit:
Thanks for reply and notices! While I was trying to do everything to get this work I missed those few things like declaring variables. Sorry for that. I threw the towel in to the cage at this point.
flow goes like this:
User sends HTML form data to app.js which is inside object of two arrays (id[] and amount[]). Amount array needs to be looped through if it has value other than 0. Same index id array value is used to fetch data from database. This data what is found from database with id from id[] is used with same index amount[] and it should be saved to mongo.
I can get the values from HTML form ok. but I have tried to make a search in Mongo in a for loop (query.exec in the code) I get the data ok. When I log the data outside the database query, variable is undefined.
I hope this clarifys a bit what I'm trying to achieve.
I'll continue this later... :)
I guess issue originates because of this function.
function ingredientIdQuery(reqBodyId) {
var ingQuery = Ingredient.find({_id:reqBodyId});
return dbQuery;
}
Is find function asynchronous or synchronous?
Also you are returning dbQuery but dbQuery does not seem to be changed inside the function.
Couple I noticed that may fix this:
You never define singleMealTempArr, so when you try to push data to it, you are gonna run into problems.
Your ingredientIdQuery function returns dbquery - which also isn't defined. You actually call it ingQuery. Even so...are you positive that this will return the data that you want?
// lets loop through all the form fields in req.body.amount
for (var i = 0; i < req.body.amount.length; i++) {
// keep going unless the form field is empty
if (req.body.amount[i] !== "") {
// assign all the form fields to the following vars
var amount = Number(req.body.amount[i]);
var singleMealTempObj = {};
singleMealTempObj.amount = amount;
var _id = req.body.id[i];
var query = ingredientIdQuery(_id);
// we are executing the ingredientIdQuery(_id), better
// double-check that this query returns the result we are
// looking for!
query.exec(function(err, ingr){
if(err) {
return console.log(err);
} else {
singleMealTempObj.ingredient = ingr[0];
// now that we've gone through and mapped all the form
// data we can assign it to the singleMealTempArr
// WOOPS! Looks like we forgot to assign it!
singleMealTempArr.push(singleMealTempObj);
}
});
}
}
}

How to load into an array all objects after Query Parse.com

I'm using Parse.com as my backend and after Query how can I fill an array with all the data inside the Parse object? how can I avoid re-mapping? example:
$scope.addContList = contacts.map(function(obj) { // re-map!!!!
return {name: obj.get("name")}; // mapping object using obj.get()
});
I'm mapping my Parse object's properties one by one: name: obj.get("name"), etc. is there a better way?
$scope.addContList = [];
var ActivityContact = Parse.Object.extend("ActivityContact2");
var query = new Parse.Query(ActivityContact);
query.equalTo("activityId", $scope.objId);
query.find({
success: function(contacts) {
console.log("Successfully retrieved " + contacts.length + " contact.");
$scope.$apply(function() {
/*$scope.addContList = contacts.map(function(obj) {
return {name: obj.get("name")}; // mapping object using obj.get()
});*/
for (var i = 0; i < contacts.length; i++) {
$scope.addContList.push(contacts.ALL_PROPERTIES); // contacts.ALL_PROPERTIES does not exist, I'm looking a way to do that and avoid mapping?
}
});
console.log("--->>>"+JSON.stringify($scope.addContList, null, 4));
},
error: function(object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and message.
}
});
Should I use Underscore library, is that the only way to go?
I have seen some ppl using PFQuery but I don't know what is that, is PFQuery better for this?
Thanks!
The other answers are correct, but I think it's unnecessary to launch a digest cycle every time you add an item from contacts to $scope.addContList. Something like this should be sufficient:
query.find({
success: function (contacts) {
$scope.apply(function () {
// 1) shallow-copy the list of contacts...
// (this is essentially what you are trying to do now)
$scope.addContList = contacts.slice();
// or 2) just assign the reference directly
$scope.addContList = contacts;
// or 3) transform the Parse.Object instances into
// plain JavaScript objects
$scope.addContList = contacts.map(function (c) {
return c.toJSON();
});
});
},
error: function (object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and message.
}
});
Options 1) and 2) will correspond to a template similar to
<div ng-repeat="cont in addContList">{{ cont.get('name') }}</div>
while option 3) can be used like
<div ng-repeat="cont in addContList">{{ cont.name }}</div>
If you change
$scope.addContList = contacts[i];
to:
$scope.addContList.push(contacts[i]);
you should be good to go. Your previous code was re-assigning addContList to be each element in the contacts array, instead of adding the element to it. So at the end of your for loop, $scope.addContList would just be the last contact in your contacts array.
Change:
$scope.addContList = contacts[i];
to
$scope.addContList.push(contacts[i]);

Multiple MongoDB queries without using a loop?

I'm trying to figure out if I can make my MongoDB queries (fron Node.js) more quicker and more efficient.
Basically I have an array of "Players", each Player has a "player_id" property. I want to iterate over each Player and use the player_id to find data about the Player in a MongoDB database.
Because of the Asynchronous nature of Node.js I have to guarantee that when I send in a query to the database, the data that I get back corresponds to the player_id that was used to query.
So far I have the following code (that I simplified). What I'm most interested in is how I can achieve this without using a for loop. Thanks.
var playersArray = new Array({"player_id":1234567}, {"player_id":9847621}, {"player_id":0946783}, {"player_id":8712890});
queryDataForPlayers(playersArray, function(data){
//Done - Each Player now has a "new_data_property" property
})
function queryDataForPlayers(playersArray){
var newPlayersArray = new Array();
var counter = 0;
for(var i=0; i<playersArray.length; i++)
{
retrievePlayerData(playersArray[i].player_id, playersArray[i], function(err,data)
{
newPlayersArray.push(data);
if(++counter == playersArray.length)
{
callback(newPlayersArray);
}//end if
});
}
}
var Schema = mongoose.model('Schema');
var ObjectID = require('mongodb').ObjectID;
function retrievePlayerData(playerID, obj, callback){
Schema.find({_id:ObjectID(String(playerID))}, function(err,data){
obj["new_data_property"] = data;
callback(err,obj);
});
}
I can't really test this, but you can pass in an array of player ID's directly to mongo, and get a document set with the related data back in just one query, something like
var playersArray = new Array({"player_id":1234567}, {"player_id":9847621}, {"player_id":0946783}, {"player_id":8712890});
var Schema = mongoose.model('Schema');
var ObjectID = require('mongodb').ObjectID;
function queryDataForPlayers(playersArray, callback){
var player_ids = playersArray.map(function(player) {
return ObjectID(String(player.player_id));
});
Schema.find({
'_id': { $in: player_ids}
}, function(err, docs){
callback(err, docs);
});
}
Use $in operator... You can use it like
Schema.find({_id:{$in:[array_of_playerid]} }).exec(function(error,results)){
}

Assemble paginated ajax data in a Bacon FRP stream

I'm learning FRP using Bacon.js, and would like to assemble data from a paginated API in a stream.
The module that uses the data has a consumption API like this:
// UI module, displays unicorns as they arrive
beautifulUnicorns.property.onValue(function(allUnicorns){
console.log("Got "+ allUnicorns.length +" Unicorns");
// ... some real display work
});
The module that assembles the data requests sequential pages from an API and pushes onto the stream every time it gets a new data set:
// beautifulUnicorns module
var curPage = 1
var stream = new Bacon.Bus()
var property = stream.toProperty()
var property.onValue(function(){}) # You have to add an empty subscriber, otherwise future onValues will not receive the initial value. https://github.com/baconjs/bacon.js/wiki/FAQ#why-isnt-my-property-updated
var allUnicorns = [] // !!! stateful list of all unicorns ever received. Is this idiomatic for FRP?
var getNextPage = function(){
/* get data for subsequent pages.
Skipping for clarity */
}
var gotNextPage = function (resp) {
Array.prototype.push.apply(allUnicorns, resp) // just adds the responses to the existing array reference
stream.push(allUnicorns)
curPage++
if (curPage <= pageLimit) { getNextPage() }
}
How do I subscribe to the stream in a way that provides me a full list of all unicorns ever received? Is this flatMap or similar? I don't think I need a new stream out of it, but I don't know. I'm sorry, I'm new to the FRP way of thinking. To be clear, assembling the array works, it just feels like I'm not doing the idiomatic thing.
I'm not using jQuery or another ajax library for this, so that's why I'm not using Bacon.fromPromise
You also may wonder why my consuming module wants the whole set instead of just the incremental update. If it were just appending rows that could be ok, but in my case it's an infinite scroll and it should draw data if both: 1. data is available and 2. area is on screen.
This can be done with the .scan() method. And also you will need a stream that emits items of one page, you can create it with .repeat().
Here is a draft code (sorry not tested):
var itemsPerPage = Bacon.repeat(function(index) {
var pageNumber = index + 1;
if (pageNumber < PAGE_LIMIT) {
return Bacon.fromCallback(function(callback) {
// your method that talks to the server
getDataForAPage(pageNumber, callback);
});
} else {
return false;
}
});
var allItems = itemsPerPage.scan([], function(allItems, itemsFromAPage) {
return allItems.concat(itemsFromAPage);
});
// Here you go
allItems.onValue(function(allUnicorns){
console.log("Got "+ allUnicorns.length +" Unicorns");
// ... some real display work
});
As you noticed, you also won't need .onValue(function(){}) hack, and curPage external state.
Here is a solution using flatMap and fold. When dealing with network you have to remember that the data can come back in a different order than you sent the requests - that's why the combination of fold and map.
var pages = Bacon.fromArray([1,2,3,4,5])
var requests = pages.flatMap(function(page) {
return doAjax(page)
.map(function(value) {
return {
page: page,
value: value
}
})
}).log("Data received")
var allData = requests.fold([], function(arr, data) {
return arr.concat([data])
}).map(function(arr) {
// I would normally write this as a oneliner
var sorted = _.sortBy(arr, "page")
var onlyValues = _.pluck(sorted, "value")
var inOneArray = _.flatten(onlyValues)
return inOneArray
})
allData.log("All data")
function doAjax(page) {
// This would actually be Bacon.fromPromise($.ajax...)
// Math random to simulate the fact that requests can return out
// of order
return Bacon.later(Math.random() * 3000, [
"Page"+page+"Item1",
"Page"+page+"Item2"])
}
http://jsbin.com/damevu/4/edit

Categories

Resources