Values won't to an array with a callback - JavaScript - javascript

I have this code:
rtclient.listFiles = function(callback) {
gapi.client.load('drive', 'v2', function() {
gapi.client.drive.files.list({
'maxResults': 20
}).execute(callback);
});
}
which I try to assign to an array using this code:
var arrayStore;
rtclient.listFiles(function(x) {
for(var i in x.items) {
arrayStore.push(x.items[i].id);
}})
However, the array arrayStore is not storing anything. This is using the Google Drive API and I am trying to access some file IDs to iterate over. Can anyone help?

the point is you should have a error like:
TypeError: Cannot call method 'push' of undefined
because you haven't defined it as an array, the other point is don't use for(...in) for arrays, you can find reasons about that in this post:
Why is using “for…in” with array iteration such a bad idea?
var arrayStore = [];
rtclient.listFiles(function(x) {
for(var i = 0; i < x.items.length; i++) {
arrayStore.push(x.items[i].id);
}
});
The other important point as #toothbrush has commented is, since the Google Drive API is asynchronous, you can't access the contents of arrayStore immediately, in other word to continue your scenario you should do something like this:
var arrayStore = [];
rtclient.listFiles(function(x) {
for(var i = 0; i < x.items.length; i++) {
arrayStore.push(x.items[i].id);
}
nextStep();
});
function nextStep(){
//here you would have access to arrayStore
console.log(arrayStore);
}

Related

My callback functions don't seem to fill in my array?

I have a problem with my callback functions. My code is supposed to make 16 GET requests to a REST API to pull 16 different JSON files. It then needs to parse each of these JSON's to a dictionary for that week's football table rankings, and ultimately save each entry into a 'dictionary of dictionaries', HistoricalTable, to give the league ranking for the past 16 weeks. However, when I run the associated callback functions, the various LeagueTable variables seem to work fine, but when I try and save these into the Historical Data, the final array appears to have the same LeagueTable entry for each, looking like this.
Here is an image of the console output for my final table. Each entry should be different, whereas each entry seems to be the most recent week.
//This creates the modifier for the URL used in the GET request
var MatchDayList = []
for (i = 0; i < 17; i++) {
MatchDayList[i] = i
}
MatchDayList.shift()
var HistoricalTable = {}
var LeagueTable = {}
// This executes the GET request
for (i = 0; i < 16; i++) {
url = 'http://api.football-data.org/v1/competitions/445/leagueTable/?matchday=' + MatchDayList[i],
$.ajax({
url: 'http://api.football-data.org/v1/competitions/445/leagueTable/?matchday=' + MatchDayList[i],
headers: {
'X-Auth-Token': ''
},
method: 'GET',
dataType: 'json',
success: function(data) {
handleData(data)
},
});
}
//This function should append the retrieved JSON to the LeagueTable variable
function handleData(data) {
for (var j = 0; j < 20; j++) {
LeagueTable[data.standing[j].position] = data.standing[j].teamName
LeagueTable[20] = data.matchday
}
saveData(LeagueTable)
}
//This function should save each LeagueTable matchday data into a bigger array, HistoricalTable
function saveData(LeagueTable) {
HistoricalTable[LeagueTable[20]] = LeagueTable
console.log(HistoricalTable)
}
You are using a single LeagueTable variable throughout the entire code. So every call to handleData populates the same LeagueTable, then tells saveData to store it in the main table. So you end up with 16 references to the same table.
To solve it, it should be enough to move the variable declaration inside handleData function:
function handleData(data) {
var LeagueTable = {};
for (var j = 0; j < 20; j++) {
LeagueTable[data.standing[j].position] = data.standing[j].teamName
LeagueTable[20] = data.matchday
}
saveData(LeagueTable)
}
On a side note, your url variable is not declared anywhere, so it ends up in the global scope, which is generally bad practice. Same with i indices inside your for loops.

JavaScript - push results from for loop into array (with SoundCloud API)

I have this function that iterates through a user's favourited track genres on SoundCloud, and then I want to push these results into an array however am having problems. Any suggestions?
SC.connect(function() {
SC.get('/me/favorites', function(favorites) {
console.log(favorites)
for (i = 0; i < favorites.length; i++) {
console.log(favorites[i].genre);
favorites[i].genre.push(meGenrePref);
}
var meGenrePref = [];
console.log(meGenrePref);
});
});
I'm not expert on SoundCloud APIs but watching your code I thought this solution:
var meGenrePref = [];
SC.connect(function(){
SC.get('/me/favorites',function(favorites) {
console.log(favorites);
for(var i = 0; i < favorites.length; i++)
{
console.log(favorites[i].genre);
meGenrePref.push(favorites[i].genre);
}
console.log(meGenrePref);
});
});
You were trying to push the contents to a wrong variable, which was the response from the API.
In this solution we are saving to meGenrePref all genres.

Clearing JS response

I am making a call to an API. The API returns a list of results. When it does so - the response is fed into an object which I then use to iterate through and display them.
Here is the function which does that:
var getAvailability = () => {
if (chosenData.hotel == "") {
showError("Please select a location before booking.");
$timeout(() => LTBNavService.setTab('location'), 50);
return;
}
searchResponse = {};
console.log(searchResponse);
WebAPI.getHotelAvailability(genSearchObject()).then((data) => {
searchResponse = data;
$timeout(() => $('[data-tab-content] .search-btn').first().focus(), 50);
generateRoomTypeObject(searchResponse);
}, (data) => searchResponse.error = data.data.errors[0].error);
};
The Problem:
The old results are still displayed until the new set of results are available. This causes a flicker and a delay which is a bad user experience.
The solution:(which i need help with)
What is the best possible way of handling this problem? Ideally, I would like to reset/clear the search response. As in, the new results are delivered and the old ones are cleared. Is this possible from within the getAvailability function?
What would be the best way to achieve this?
The Solution:
Thanks to #Daniel Beck for his suggestion to call the generateRoomTypeObject function and feed it an empty object - +1'd his comment.
This triggered an undefined error in my generateRoomTypeObject function where i was running a few length checks(makes sense, because the object was empty - so there was nothing to do length checks on).
I handled the error by handling the undefined exception and setting the searchResponse to an empty object.
var generateRoomTypeObject = (searchResponse) => {
var ratePlans = searchResponse.ratePlans,
errors = searchResponse.error,
roomTypes = [],
ignoreBiggerRooms = false;
rawRoomsObjs = [];
if (angular.isUndefined(errors)) {
// Iterate over the rate plan
if(ratePlans === undefined){
//generateRoomTypeObject -- Handle undefined by creating new object
searchResponse = {}
}else{
for (var i = 0; i < ratePlans.length; i++) {
var ratePlan = ratePlans[i],
rooms = ratePlan.rooms;
// Iterate over the rooms and add rooms to room object. Also keep a list of room types.
for (var j = 0; j < rooms.length; j++) {
//Stuff here
}
}
}
}

AngularJS - Is controller code executed line by line?

I'm new to AngularJS, and is experimenting AngularJS with Twitch API.
I have a list of channels that I'm interested in, defined as var channels.
Then I use the $http.get function to loop through another array, twitchList.channels, which contains the API addresses that I'm supposed to call.
(function() {
var app = angular.module('twitchList', []);
app.controller('twitchController', ['$http', function($http){
var twitchList = this;
twitchList.channels = [];
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff", "MedryBW"];
for (var i = 0; i < channels.length; i++ ) {
twitchList.channels.push({
name: channels[i],
api: 'https://api.twitch.tv/kraken/streams/' + channels[i],
})
}
var data_list = [];
for (var j = 0; j < twitchList.channels.length; j++) {
$http.get(twitchList.channels[j].api).success(function(data){
data_list.push(data);
})
}
// Issue arises here!
console.log(data_list);
console.log(data_list.length);
}]);
})();
The API calls seems to be working perfectly, however, I need to get the results of the API call into an array, called data_list. Now, when I print data_list, and data_list.length, what happens is that data_list.length always returns 0, and data_list is sometimes populated (meaning it's either 0 size array or 9 size array). Even though the property of the array has a length 9, but calling .length always gives 0.
This let me think that the controller code is not executed line by line? Or is there something wrong with my logic?
Can someone give me a pointer? Thanks
No, this line:
data_list.push(data);
will be executed when you receive a response on the http request sent a line above. Hence the following lines:
console.log(data_list);
console.log(data_list.length);
will output [] and 0
I've not used it before, but could you possibly use $q.all in order to resolve multiple promises? I've used the equivalent $.when function in jQuery to achieve this in the past.
var data_list = [];
var promise_array = [];
var request;
for (var j = 0; j < twitchList.channels.length; j++) {
request = $http.get(twitchList.channels[j].api);
request.success(function(data) {
data_list.push(data);
});
promise_array.push(request);
}
$q.all(promise_array).then( function() {
console.log(data_list);
console.log(data_list.length);
});

HTML rendering before for-loop finishes executing, only first item in for-loop showing

I am trying to render an html page that contains all of the posts that a user has received. Right now the issue I am having (shown under Way 1) is that when I call the function renderPosts after the web socket is received, only the first post in the array is rendered (the array has more than one post in it).
On the other hand, Way 2 in which I have no for loop and instead manually render each post works in that all four posts are rendered. But I need to be able to work with an arbitrary number of posts which is why I need to use the for loop.
I am using socket.io and javascript.
Way 1:
socket.on('postsToRender', function(arrayOfPostsToRender) {
renderPosts(arrayOfPostsToRender);
});
function renderPosts(arrayOfPostsToRender) {
for (var index = 0; index < arrayOfPostsToRender.length; index++) {
renderPost(arrayOfPostsToRender[index]);
}
}
function renderPost(postToRender) {
var feed = document.getElementById("feed");
var postContent = document.createTextNode(postToRender.content);
var post = document.createElement("div");
post.appendChild(postContent);
feed.appendChild(post);
}
Way 2:
socket.on('postsToRender', function(arrayOfPostsToRender) {
renderPost(arrayOfPostsToRender[0]);
renderPost(arrayOfPostsToRender[1]);
renderPost(arrayOfPostsToRender[2]);
renderPost(arrayOfPostsToRender[3]);
});
function renderPost(postToRender) {
var feed = document.getElementById("feed");
var postContent = document.createTextNode(postToRender.content);
var post = document.createElement("div");
post.appendChild(postContent);
feed.appendChild(post);
}
Try this:
function renderPosts(arrayOfPostsToRender) {
for (var index = 0; index < arrayOfPostsToRender.length; index++) {
(function(i){
renderPost(arrayOfPostsToRender[i]);
})(index);
}
}

Categories

Resources