I have vs.selectedTags which is an array with 3 objects.
In my for loop which will run 3 times, I need to make 3 API calls to get the tickers data for each object which I'm able too.
My problem comes when I try to assign those tickers to each vs.selectedTags[i].tickers object in the array.
It can't iterate over the i inside of the ApiFactory call. i becomes 3, and I have to cheat by using [i-1] to stop it from erroring out. However i still stays stuck at 2 so it always saves the last tickers data to all the items in my vs.selectedTags array.
var vs = $scope;
for (var i = 0; i < vs.selectedTags.length; i++) {
console.log(i);
vs.selectedTags[i].tickers = '';
console.log(vs.selectedTags[i].tickers);
ApiFactory.getTagData(vs.chosenTicker, vs.selectedTags[i].term_id).then(function(data) {
// console.log(data.data.ticker_tag);
console.log(data.data.ticker_tag.tickers);
console.log(i-1);
// console.log(vs.selectedTags[0]);
// How would you properly iterate [0 - 1 - 2] here?
vs.selectedTags[i-1].tickers = data.data.ticker_tag.tickers;
console.log(vs.selectedTags[i-1]);
});
}
You need a closure / new scope, as the ApiFactory.getTagData function is asynchronous
for (var i = 0; i < vs.selectedTags.length; i++) {
(function(j) {
vs.selectedTags[j].tickers = '';
ApiFactory.getTagData(vs.chosenTicker, vs.selectedTags[j].term_id).then(function(data) {
vs.selectedTags[j].tickers = data.data.ticker_tag.tickers;
});
})(i);
}
if you put the stuff inside of your for loop in a separate function it will fix your closure issue. so:
var bob = function(i){
console.log(i);
vs.selectedTags[i].tickers = '';
console.log(vs.selectedTags[i].tickers);
ApiFactory.getTagData(vs.chosenTicker, vs.selectedTags[i].term_id).then(function(data) {
// console.log(data.data.ticker_tag);
console.log(data.data.ticker_tag.tickers);
console.log(i);
// console.log(vs.selectedTags[0]);
// How would you properly iterate [0 - 1 - 2] here?
vs.selectedTags[i].tickers = data.data.ticker_tag.tickers;
console.log(vs.selectedTags[i]);
});
}
for (var i = 0; i < vs.selectedTags.length; i++) {
bob(i);
}
Related
I ask this question again as user Cerbrus have marked the previous question as a duplicate of this question.
Can someone be so kind to show me how the question indicated by this user, should solve the code below? I can't find a match between those situations (even thought they are similar).
I need to pass a variable to a function inside a for loop. Here's an example:
var mainObj = [],
subArr = ['val1', 'val2'],
tmp;
for (var i = 0; i < subArr.length; i++) {
tmp = subArr[i];
mainObj.push({
key: function(varsFromLibrary) {
myFunc(tmp);
}
});
}
Here I have 2 problems:
why do i have to assign subArr[i] to tmp? Using myFunc(subArr[i]) will return that i is undefined?
why in myFunc i only receive the last value of subArr array?
UPDATE
I've updated the code as follows but i get TypeError: funcs[j] is not a function
var mainObj = [],
subArr = ['val1', 'val2'],
tmp,
funcs = [];
function createfunc(i) {
return function() { console.log("My value: " + i); };
}
for (var i = 0; i < subArr.length; i++) {
funcs[i] = createfunc(subArr[i]);
}
for (var j = 0; j < subArr.length; j++) {
tmp = subArr[i];
mainObj.push({
key: function(varsFromLibrary) {
funcs[j]();
}
});
}
Simply use let :
for (var i = 0; i < subArr.length; i++) {
let tmp = subArr[i];
mainObj.push({
key: function(varsFromLibrary) {
myFunc(tmp);
}
});
}
Or why cant you simply copy the value into the object?:
for (var i = 0; i < subArr.length; i++) {
mainObj.push({
tmp:subArr[i],
key: function(varsFromLibrary) {
myFunc(this.tmp);
}
});
}
Another try of explaining:
Lets imagine youre a byciclist. You want to measure your speed so you ask 10 friends of you to stand next to the route at certain points and to tell you your speed. Some pseudocode:
const friends = [];
var speed = 20;//youre really fast
for(var point = 1; point < 10; point++){
speed -= 2;//youre slowing down
friends.push({
ask(){
console.log(point, speed);
}
});
}
Now afterwards you stand at the last point 10 together with your friends and you ask them for the current speed and the point they stay at. What will they tell you? Exactly, they are all standing next to you at point 10 and your current speed is 0. You asked them for the current speed and not to remember the current speed. If you want them to remember it, they need to write it down:
friends.push({
speed,//every friend object has the current value stored
point,
ask(){ console.log(this.speed,this.point)}
});
Or you need to create 10 parallel universes your friends stay in, so if you ask them for your speed they will still see you driving next to them:
for(let point = 1; point < 10; point++){
let localspeed = (speed -= 2);//youre slowing down
why do i have to assign subArr[i] to tmp?
You don't. That isn't the solution proposed by the duplicate question.
Using myFunc(subArr[i]) will return that i is undefined?
i won't be undefined. It will be the same as subArr.length.
subArr[i] will be undefined, because subArr.length is the number of items in the array and the array is zero indexed.
why in myFunc i only receive the last value of subArr array?
Because that is the last value you copied to tmp before the loop ended.
As the high rated answer on the question you link to says, you need to copy i or subArr[i] to a new scope so it won't change next time you go around the loop.
I have an array of shots. I have been able to take that array and loop through it to get all shots that occurred on hole #1 and then rearrange them in order based on "shot_number". I now need to do this for every hole and to create an array for each hole (ex: holeArray1, holeArray2). I have attempted a number of solutions to increment x but if I do I end up missing some shots that occurred on certain holes.
How can I refactor this function to create this array for every hole without just copying and pasting the code and changing the variable x myself? Thank you for your help. I know I should be able to figure this one out but am struggling.
$scope.createHoleShotsArrays = function () {
var i = 0;
var x = 1;
var holeArray = [];
var len = $scope.shots.length;
for (; i < len; i++) {
if ($scope.shots[i].attributes.hole == x) {
holeArray.push($scope.shots[i]);
holeArray.sort(function (a, b) {
if (a.attributes.shot_number > b.attributes.shot_number) {
return 1;
}
if (a.attributes.shot_number < b.attributes.shot_number) {
return -1;
}
// a must be equal to b
return 0;
});
}
}
console.log(holeArray);
};
Push the items you want into arrays, and sort them once. I don't have cases to test the code. You may modified it a little if something goes wrong.
$scope.createHoleShotsArrays = function() {
var holeArrays = [];
$scope.shots.forEach(function(shot) {
if (holeArrays.length < shot.attributes.hole) {
holeArrays[shot.attributes.hole - 1] = [];
}
holeArrays[shot.attributes.hole - 1].push(shot);
});
holeArrays.forEach(function(arr) {
arr.sort(function(a, b) {
return a.attributes.shot_number - b.attributes.shot_number;
});
});
console.log(holeArrays);
};
I'm trying to iterate through an array of RSS feeds as below:
var rssFeeds = [ ['http://www.huffingtonpost.com/tag/womens-rights/feed/', "Huffington Post"], ['http://abcnews.go.com/topics/urss?pageid=681900', "ABC News"], ['http://www.globalissues.org/news/topic/166/feed', "Global Issues"], ['http://topics.nytimes.com/top/reference/timestopics/subjects/f/feminist_movement/index.html?rss=1', "The New York Times"] ];
This array contains both the location of the feed and a string with the name of the feed. I iterate through the array with a for loop as below, the one with i as an iterator.
Once I receive the results, I iterate through the results in the callback function with the for loop j. I append each fetched result to another array entryArray, and after appending each result, I add a new attribute 'source' to that fetched result using the name for the RSS feed.
function loadEntries()
{
var feedr = new Array();
for( var i = 0; i < rssFeeds.length; i++ )
{
feedr[i] = new google.feeds.Feed( rssFeeds[i][0] );
feedr[i].setNumEntries(loadAmount);
feedr[i].load(function(result) {
if (!result.error) {
console.log(i);
for (var j = 0; j < result.feed.entries.length; j++) {
entryArray[entryArray.length] = result.feed.entries[j];
entryArray[entryArray.length - 1]['source'] = rssFeeds[i][1];
}
}
});
}
}
However, and this is where the problem arises, the iterator I use (i) to indicate the name to append is always equal to rssFeeds.length, because the callback functions for all four load commands occur after the initial for loop has already finished iterating. The console.log(i); you see always returns 4.
This worked when I copied and pasted the code for each item individually, but I'd rather not copy and paste because the RSSFeeds array will probably be much longer in the future. Is there any way I can accomplish this with a loop?
Consider the following JSFiddle example
The reason your code does not function as intended is because the value of the variable i, will be i = 4 at the end of the loop and not 0,1,2,3 as desired because this would already have happened been incremented by the for loop.
The trick is to use a recursive function and your load function will function similarly to:
feedr.load(function (result) {
if (!result.error) {
console.log(i);
for (var j = 0; j < result.feed.entries.length; j++) {
entryArray[entryArray.length] = result.feed.entries[j];
entryArray[entryArray.length - 1]['source'] = rssFeeds[i][1];
}
if (rssFeeds.length - 1 > i) {
loadEntries();
i++;
}
}
});
So first of all, I'd like to thank nd_macias for providing me with the links that helped me find this solution. Basically, I wrapped the load function in a function, and then called that function with the for loop as below:
function loadEntries()
{
var feedr = new Array();
for( var i = 0; i < rssFeeds.length; i++ )
{
feedr[i] = new google.feeds.Feed( rssFeeds[i][0] );
feedr[i].setNumEntries(loadAmount);
var f = function(n) {
feedr[n].load(function(result) {
if (!result.error) {
for (var j = 0; j < result.feed.entries.length; j++) {
entryArray[entryArray.length] = result.feed.entries[j];
entryArray[entryArray.length - 1]['source'] = rssFeeds[n][1];
}
}
});
}
f(i);
}
}
Quick bit about my background:
-been learning for about 3 months;
-work in tech support for a small software company. 2 years exp.
-a lot of knowledge is secondhand and I am still learning the basics
I am trying to create an object every second. The object is created directly to the last position of an array that remembers a set quantity of objects created before the most recent one
function Fruit(name, position) {
this.name = name;
this.position = position;
}
var showXMostRecentFruits = 20;
var fruitCounter = 0;
function generateName() {
var name = 'Experimental Fruit' + fruitCounter;
return name;
}
var fruitsArray = [];
function shiftFruits() {
for (i = 0; i < showXMostRecentFruits; i++) {
fruitsArray[i] = fruitsArray[i + 1];
}
function updateFruitPositions() {
for (i = 0; i < showXMostRecentFruits; i++) {
fruitsArray[i].position = i;
}
}
var fruitTimer; //used for setting and clearing setTimeout
function createNewFruit() {
shiftFruits();
fruitsArray[showXMostRecentFruits - 1] = new Fruit(generateName());
updateFruitPositions();
fruitCounter += 1;
fruitTimer = setTimeout(function() {
createNewFruit();
}, 1000);
}
Say the function createNewFruit() is run once
createNewFruit();
Then I try to pull some meaning from the array
console.log(fruitsArray[19];
All I get is:
Fruit {}
undefined
This issue is when I want to run a loop (see updateFruitPositions()) that updates a propery of each object in the array, an error is returned that the objects are undefined. I get that they are undefined because they are not assigned to unique variables (at least not that I'm aware of). How can I identify the objects or how can I create unique containers for them so I access them in the array?
You need to test whether a given element is set to something before attempting to write to one of its properties.
Instead of this...
for (i = 0; i < showXMostRecentFruits; i++) {
fruitsArray[i].position = i;
}
Use this:
for (i = 0; i < showXMostRecentFruits; i++) {
if (fruitsArray[i])
fruitsArray[i].position = i;
}
You fill the array from the end, staring with element 20. Without the if (fruitsArray[i]), you're attempting to set undefined.position = i for the first 19 elements.
You could replace the showFruits function with something much more efficient:
function shiftFruits() {
if (fruitsArray.length > showXMostRecentFruits) {
fruitsArray.shift();
}
}
and updateFruitPositions only needs to update members that exist, the length is controlled by shiftFruits:
function updateFruitPositions() {
for (i = 0; i < fruitsArray.length; i++) {
fruitsArray[i].position = i;
}
}
or where forEach is supported:
function updateFruitPositions() {
fruitsArray.forEach(function(fruit, i){fruit.position = i});
}
so it only visits members that exist. And the createNewFruit has:
fruitsArray.push(new Fruit(generateName());
I'm creating a matching game and I'm trying to add a class from an array to match against.
The code I have below creates the classes I need, then randomizes them.
My problem is in the randomizeDeck function. I'm trying to add each of the classes to the specified element twice. When I console.log the code the classes gets added to the first six elements but not the last six, which I need it to do so that I have the classes to match against in the matching game I'm creating.
var cardDeck = new Array();
function createDeck() {
for (i = 1; i <= 6; i++) {
cardDeck.push("card-" + i);
}
}
createDeck();
var randDeck = cardDeck.sort(randOrd);
function randomizeDeck() {
card.each(function(i){
$(this).addClass(randDeck[i]);
});
}
randomizeDeck();
I think your createDeck function needs to create 12 classes instead of 6. Just push each one twice:
function createDeck() {
for (i = 1; i <= 6; i++) {
cardDeck.push("card-" + i);
cardDeck.push("card-" + i);
}
}
Then you'll have an array of 12 classes (2 each of 6 unique classes), which will be randomized and assigned to the 12 cards.
I suggest a separate variable to keep track of the index, rather that the each index. Once you've gone through the pack once, it might be a good idea to shuffle the deck again so the order is different on the second pass. YMMV.
function sortCards(randOrd) {
randDeck = cardDeck.sort(randOrd);
}
function randomizeDeck() {
var count = 0;
cards.each(function(i) {
if (i === 6) { count = 0; sortCards(randOrd); }
$(this).addClass(randDeck[count]);
count++;
});
}
Your randomizeDeck() function can be rewritten to use the same array of class names twice:
function randomizeDeck() {
card.each(function(i){
if(i < 6)
$(this).addClass(randDeck[i])
else
$(this).addClass(randDeck[i-6]);
});
}
Note: I would rewrite the variable card as $cards so that you know it's a jQuery object and in this case a collection of them. Otherwise, its hard to tell it apart from any other javascript var.
Try something like this - it's tested now updated
SEE THIS FIDDLE
http://jsfiddle.net/8XBM2/1/
var cardDeck = new Array();
function createDeck() {
for (i = 1; i <= 6; i++) {
cardDeck.push("card-" + i);
}
}
createDeck();
var randDeck = cardDeck.sort();
alert(randDeck);
function randomizeDeck() {
var x = 0;
$('div').each(function(i){
if ( i > 5) {
$(this).addClass(randDeck[x]);
x++;
} else {
$(this).addClass(randDeck[i]);
}
});
}
randomizeDeck();