Play functionality in nested loops Javascript - javascript

I am trying to build play functionality using JavaScript to play different colors for my attribute values on map. The basic logic, I am building using simple for loop does not work for me as the loop is played so fast and I only get display last value on Map.
Example:
//Hash of my Values
var myHash = {};
myHash['1'] = [10,100];
myHash['2'] = [20,200, 30];
myHash['3'] = [40,300, 4, 5];
function startPlaying() {
for (item in myHash) {
var myValues= myHash[item];
var timeOut=setTimeout(function(){
animate(myValues,item);
},1000);
}
}
function animate(myValues,item) {
for(i in myValues) {
//calling my function for each value to play on map with different styles.
playMyMap();
}
}
I was expecting here that my function startPlaying will fire timeout function three times(for number of items with 1 sec delay) with corresponding myValues and item and It will keep on iterating over myValues individually for three items.
But this doesn't work for me beacuse the myValues gets messed up while each call of setTime out function.
Could anyone please give me an idea here, how can I build such functionality?

var timeOut=setTimeout(function(){
animate(myValues,item);
},1000);
}
Will trigger 3 times, but all after 1 second. If you keep a counter you have to do the 1000*counter to have 1 animate function each second.
The below example uses the item as the multiplyer because you start your index with 1. Normally array's start at index 0. If you change your first item to index 0, just do 1000 * (item + 1) to start with 1 second.
Try this:
function startPlaying() {
for (item in myHash) {
var myValues= myHash[item];
var timeOut=setTimeout(function(){
animate(myValues,item);
},1000 * item );
}
}

Related

How to generate a new random number (that's different from the previous random number)

I'm trying to change the following (that currently returns a random number from an array), so that each random number is different from the last one chosen.
function randomize(arr) {
return arr[Math.floor(Math.random()*arr.length)];
}
oracleImg = [];
for (var i=1;i<=6;i++) {
oracleImg.push(i);
}
randOracleImg = randomize(oracleImg);
I tried the following, but it's not always giving me a number different from the last number.
function randomize(arr) {
var arr = Math.floor(Math.random()*arr.length);
if(arr == this.lastSelected) {
randomize();
}
else {
this.lastSelected = arr;
return arr;
}
}
How can I fix this?
Your existing function's recursive randomize() call doesn't make sense because you don't pass it the arr argument and you don't do anything with its return value. That line should be:
return randomize(arr);
...except that by the time it gets to that line you have reassigned arr so that it no longer refers to the original array. Using an additional variable as in the following version should work.
Note that I've also added a test to make sure that if the array has only one element we return that item immediately because in that case it's not possible to select a different item each time. (The function returns undefined if the array is empty.)
function randomize(arr) {
if (arr.length < 2) return arr[0];
var num = Math.floor(Math.random()*arr.length);
if(num == this.lastSelected) {
return randomize(arr);
} else {
this.lastSelected = num;
return arr[num];
}
}
document.querySelector("button").addEventListener("click", function() {
console.log(randomize(["a","b","c","d"]));
});
<button>Test</button>
Note that your original function seemed to be returning a random array index, but the code shown in my answer returns a random array element.
Note also that the way you are calling your function means that within the function this is window - not sure if that's what you intended; it works, but basically lastSelected is a global variable.
Given that I'm not keen on creating global variables needlessly, here's an alternative implementation with no global variables, and without recursion because in my opinion a simple while loop is a more semantic way to implement the concept of "keep trying until x happens":
var randomize = function () {
var lastSelected, num;
return function randomize(arr) {
if (arr.length < 2) return arr[0];
while (lastSelected === (num = Math.floor(Math.random()*arr.length)));
lastSelected = num;
return arr[num];
};
}();
document.querySelector("button").addEventListener("click", function() {
console.log(randomize(["a","b","c","d"]));
});
<button>Test</button>
Below code is just an example, it will generate 99 numbers and all will be unique and random (Range is 0-1000), logic is simple just add random number in a temporary array and compare new random if it is already generated or not.
var tempArray = [];
var i=0;
while (i != 99) {
var random = Math.floor((Math.random() * 999) + 0);
if (tempArray.indexOf(random)==-1) {
tempArray.push(random);
i++;
} else {
continue;
}
}
console.log(tempArray);
here is a version which will ensure a random number that is always different from the last one. additionally you can control the max and min value of the generated random value. defaults are max: 100 and min: 1
var randomize = (function () {
var last;
return function randomize(min, max) {
max = typeof max != 'number' ? 100 : max;
min = typeof min != 'number' ? 1 : min;
var random = Math.floor(Math.random() * (max - min)) + min;
if (random == last) {
return randomize(min, max);
}
last = random;
return random;
};
})();
If you want to ALWAYS return a different number from an array then don't randomize, shuffle instead!*
The simplest fair (truly random) shuffling algorithm is the Fisher-Yates algorithm. Don't make the same mistake Microsoft did and try to abuse .sort() to implement a shuffle. Just implement Fisher-Yates (otherwise known as the Knuth shuffle):
// Fisher-Yates shuffle:
// Note: This function shuffles in-place, if you don't
// want the original array to change then pass a copy
// using [].slice()
function shuffle (theArray) {
var tmp;
for (var i=0; i<theArray.length;i++) {
// Generate random index into the array:
var j = Math.floor(Math.random()*theArray.length);
// Swap current item with random item:
tmp = theArray[i];
theArray[j] = theArray[i];
theArray[i] = tmp;
}
return theArray;
}
So just do:
shuffledOracleImg = shuffle(oracleImg.slice());
var i=0;
randOracleImg = shuffledOracleImg[i++]; // just get the next image
// to get a random image
How you want to handle running out of images is up to you. Media players like iTunes or the music player on iPhones, iPads and iPods give users the option of stop playing or repeat from beginning. Some card game software will reshuffle and start again.
*note: One of my pet-peeves is music player software that randomize instead of shuffle. Randomize is exactly the wrong thing to do because 1. some implementations don't check if the next song is the same as the current song so you get a song played twice (what you seem to want to avoid) and 2. some songs end up NEVER getting played. Shuffling and playing the shuffled playlist from beginning to end avoids both problems. CD player manufacturers got it right. MP3 player developers tend to get it wrong.

How can I get this function to be called a specific amount of times?

I would like to call a function a specific amount of times. I would like the number of times I would like my function to be called in an array as seen below. I have a for loop that goes through the array and I am attempting to call the function named "thefunction"
as many times as the number in the array item. So the first item in the array is 8, so I first want "thefunction" function to be called 8 times which means I should see the alert message "thefunction" will display, 8 times and then 2 times as 2 is the next item in the array then 15 times. I'd also like to pause for a moment in between each set of calls. So after the function is called 8 times, it will pause for a moment before calling the function 2 times then again before it calls the function 15 times and on and on. Here is my code so far.
var thearray = ['8','2','15'];
for(j=0; j < thearray.length; j++){
num = thearray[j];
var counter = 1;
(function foo() {
thefunction();// function I'm calling
if (counter < num) {
counter++;
setTimeout(foo, 400);
}
})();
}
function thefunction (){
alert('test');
}
You can't use the for loop with the variable j and use an asynchronous setTimeout(). The for loop will run to completion and the j variables will have already increased before the setTimeout() runs. This is a classic problem with trying to use a for loop index from an asynchronous function.
I don't understand exactly what you're trying to accomplish so I'm not sure exactly what code to suggest. You have three values. Are you trying to do two loops, one to go through the array and one to process each array value? Can you explain better what the final output should be?
I'm guessing here, but if what you're trying to do is to call your function 8 times, then pause, then 2 times then pause, then 15 times, then be done, you can do this:
function runArray(arr, fn) {
// initialize array index - can't use for loop here with async
var index = 0;
function next() {
var cnt = +arr[index];
for (var i = 0; i < cnt; i++) {
fn(index, cnt);
}
// increment array index and see if there's more to do
++index;
if (index < arr.length) {
setTimeout(next, 400);
}
}
// start the whole process if the array isn't empty
if (arr.length) {
next();
}
}
var theArray = ['8','2','15'];
runArray(theArray, thefunction);
Working demo: http://jsfiddle.net/jfriend00/Loycmb3b/
FYI, if what you're putting in the array are meant to be used as numbers, it's better to put them in the array as numbers, not as strings. I've made my code work either way, but it's more efficient and just better code to use numbers when you mean numbers.

Track which JSON objects have been output

I'm outputting 20 or so JSON objects randomly by setting the index to a randomNumber() initially when the page is loaded.
I'm refreshing each JSON object individually that has already been output on a timeInterval.
To keep track of which JSON items have been output I am storing the index of each item into an array via arrUsed.push[index]
Now trying to write the function that will update() each JSON objects individually and am currently stuck on how I can update the each div with the information from a new JSON object that has not already been output (pushed to the arrUsed[]).
Here's the function I have so far:
function reloadItems() {
var itemTotal = $('div.item').length; // Total number of items loaded initially
var randomNumber=Math.floor(Math.random()*301) //returns number
index = randomNumber; // Sets index to be used in JSON data to random number
}
The array that contains the already output index's is declared globally: arrUsed = []; Each item that is output initially when the page load is being stored to the array fine, so that part is covered. It's a matter of choosing a random JSON object, checking to ensure it is not in the array/not been output already, and then updating the div on the page.
Here's the previous question that has led me to this point
Here's a working example of a JSON/AJAX Ticker:
http://ticker.weisser.co/
Per twhyler's specification, it randomly swaps an item every 4.5 seconds, keeping track of ones that have already been seen. Once they've all been seen, it starts over again:
Code Files:
default.html (Main Program)
template.html (Product Template)
UK.top.20.html (JSON Data)
ticker.js (jQuery)
ticker.css (Style Sheet)
First, we should store template.html in our global template variable and fire the getJson() function:
template = '';
....
$(document).ready(function () {
$.get('template.html', function(html) { template = html; getJson(); });
});
Then, we'll store the JSON into our data variable and fire the initialize() function:
data = ''; // JSON data will be stored here
myurl = 'UK.top.20.html';
....
function getJson() {
$.getJSON(myurl, function (JSON) { data = JSON; initialize(); });
}
Here, we'll call the load() function 3 times to populate our 3 product div's with data right away. Then we set i back to 2 (that's so it will change the 3rd DIV first), and schedule tick() to fire in 4.5 seconds:
tick_time = 4500;
....
function initialize() { // Start with the first 3
for (i = 1; i < 4; i++) { load(); }
i = 2;
setTimeout('tick()', tick_time);
}
Before we explain the load() function, let's talk about `String.prototype.f' at the bottom of the script:
String.prototype.f = function () { var args = arguments; return this.replace(/\{(\d+)\}/g, function (m, n) { return args[n]; }); };
This function works similar to String.Format(formatstring, arg1, arg2, arg3...) in C# or sprintf($formatstring, arg1, arg2, arg3...) in PHP. Here's an example:
formatstring = 'Roses are {0}, Violets are {1}, String.Format {2} and so do {3}!';
result = formatstring.f('red', 'blue', 'rocks', 'you');
alert(result);
So as you can see, this String.prototype.f function comes in very handy!
The first thing the load() function will do is set rid = randomId();. Let's take a look at the randomId() function next. The first thing it does is get a number from 1-20 (based on the length of our JSON data). Then, it checks to see if our seen array is the same size as our JSON data, and if it is - it sets it back to 0. Next it makes sure that our rid hasn't been seen recently, and if it has, the function recursively calls itself again till it gets a unique random id. Otherwise, we store the rid in our seen array and return the value.
function randomId() {
rid = Math.floor(Math.random() * data.results.length);
if (seen.length == data.results.length) { seen.length = 0; }
if ($.inArray(rid, seen) == -1) {
seen.push(rid);
return rid;
} else { return randomId(); }
}
Next in our load() function after getting that random ID, we setup item and plat as convenient shortcuts. plat_html is a temporary storage place for the Platform elements. The for-loop looks at all the Platform data in our JSON and uses our String.Format function to fill our plat_html string.
Finally, we allow the current value of i (which is global) determine which #product_ gets updated, and template.f fills our template with JSON data which is done with a smooth jQuery animation thanks to .fadeIn().
function load() {
rid = randomId();
item = data.results[rid];
plat = item.Platform;
plat_html = '';
for (var j = 0; j < plat.length; j++) {
plat_html += plat_fmt.f(
plat[j].Hardware,
plat[j].Market
);
}
$('#product_' + i).html(
template.f(
item.Image,
item.id,
item.AgeRating,
item.WikiUrl,
item.Title,
item.Source,
item.Genre,
plat_html
)
).fadeIn();
}
Lastly, let's take a look at the recursive tick() function. It begins by incrementing our global i variable and setting it back to 1 when it reaches 4. Then we perform an animated fadeOut() on our #product_ element and wait till it's finished till we call load() again. Then, it schedules itself to run again in another 4.5 seconds.
function tick() {
i++; if (i == 4) { i = 1; }
$('#product_' + i).fadeOut(function() { load(); });
setTimeout('tick()', tick_time);
}
That's it!
Use $.inArray(): http://api.jquery.com/jQuery.inArray/
$.inArray(indexInQuestion, arrUsed);
It will return -1 if the element is not in the array, so you will know wether indexInQuestion was already added to arrUsed.

Show values from array in jquery

Ok, the title might make it sound easy (and maybe it is), but I can't figure out how to show values from an array, not just do a each with them, but put them on the site slowly, with a nice effect...
Like this Twitter widget, but from an array (maybe wait 2sec, and when throw another value from the array)?
My array contains another array, with 4 values, is it possible to show the first 2 values, wait about 2 sec, and then show the last 2 values? Now when the last two values is out (from the prev array), wait 2 more seconds, and show the next array (again with four values, first show 2, wait 2 sec, and show the next 2, and so on...)
You can do this easily using setInterval or a chained series of setTimeout. I tend to prefer the latter, which looks something like this:
function showValues(a, delay) {
var index = 0;
if (a.length !== 0) {
// If you want a delay before the first one:
setTimeout(showValue, delay);
// Alternately, if you want to show the first right away
// showValue();
}
// Function to show one value
function showValue() {
// Get the value
var value = a[index++];
/* ...show the value however you see fit... */
// Schedule next display, if any
if (index < a.length) {
// Schedule next run
setTimeout(showValue, delay);
}
}
}
Usage:
showValues(["value1", "value2", /* etc. */, 2000); 2000 = two seconds
Live example | source
Animation functions like fadeIn() make use of the jQuery FX queue so you can use .delay() in the call chain to delay the calling of these functions. In the following example one element is shown every second.
var data = ['foo', 'bar', 'foobar', 'moo', 'meow', 'xxx'];
var list = $('ul');
$.each(data, function(i, value) {
$('<li/>', { text: value }).hide().appendTo(list).delay(i * 1000).fadeIn();
});
Demo: http://jsfiddle.net/ThiefMaster/frW8s/

Extract array values with last extraction memory on callback in javascript

Let's say I have a dynamic array that is populated on page load with various amounts of strings inside:
var arr = ["string1","string2","string3","string4","string5","string6","string7","string8","string9","string10","string11","string12","string13","string14","string15","string16","string17","string18"];
I then have a function that is called on each event (let's say a click) that needs to bring back 3 strings of the array consecutively while remembering which value it left off at, the last time it was called. So:
First time function is called, it returns:
string1, string2, string3
Second time it is called, it returns:
string4, string5, string6
and so on...
I don't the need the code for the click event or the callback function, rather the code for the function that would generate the extraction each time and bring it back. Something simple like being able to call:
arr.runExtraction();
on each callback and having it bring back the desired data.
What should happen if the array is exhausted? Start from the beginning?
You could do something like this:
function get_iterator(values, steps, start) {
steps = steps || 1;
var current = start || 0,
max = values.length;
return function() {
var end = current+steps,
end = end > max ? max : end,
t = values.slice(current, end);
current = end % max;
// or if you don't want to wrap around:
// current = end;
return t;
}
}
Edit: Added start parameter.
As current will be the same as values.length in the end, splice will return an empty array if you don't wrap around.
Using slice won't change the original array.
And then
var extract = get_iterator(arr, 3);
var arr1 = extract(); // gives you the first three elements
var arr2 = extract(); // gives you the next three elements etc.
DEMO
It might give you less elements in the "last" extraction if the number of elements is not divisible by number of extracted elements. The next call will let it start from the beginning again. You could also modify it that it wraps around and takes elements from the beginning so that it will always return as many elements as you have specified.
Reference: Array.prototype.slice
var arr = ["string1","string2","string3","string4","string5","string6","string7","string8","string9","string10","string11","string12","string13","string14","string15","string16","string17","string18"];
var runExtraction = function () {
return arr.splice(0, 3);
}

Categories