Extract array values with last extraction memory on callback in javascript - 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);
}

Related

Get second element of array after removing elements and updating array

Task: I have var = array; in a function that can hold the following sample array - the items represent time (seconds):
["20.30", "30.55", "8.25", "32.74", "2.75", "39.24"]
A setInterval loop displays the third item (array[2]) when a timer exceeds or matches second item in array (array[1]). Currently, after that happens, the first two items of array (array[0] and array[1]) are removed and array variable is re-indexed like so, looping the process until done:
array = array.splice(2).filter(function(){return true;});
Dilemma: I thought it would be easiest to just remove the first two items and update the array this way so as to simply apply a condition like so: if ( array[1] <= timer ){ but the splice() and filter() methods - which apply after the conditional statement in the loop - don't seem to re-index array variable.
Is there a way to make sure the array variable is re-indexed by new length, or perhaps there is a better way by navigating through the array variable without having to mutate/update it? The latter would perhaps be best, but I am kind of noob in better array manipulation and would appreciate some help.
UPDATE
Below I summarize what I clearly wish to attain:
var array = ["20.30", "30.55", "8.25", "32.74", "2.75", "39.24", etc...]
Instead of programmatically fetching each and every item iteration (impossible since amount will dynamically change) like follows ...
timelinePolling = setInterval(function(e){
...
if ( array[1] <= timer ){
timeline(array[2]); // Apply 3rd item timeline value
} else if ( array[3] <= timer ){
timeline(array[4]); // Apply 5th item timeline value
}
// etc, for remaining determined/undetermined items.
}, 30);
... I wish to iterate and dynamically increment the index target of array item variable (what is in brackets) the moment array[index] <= timer is achieved, like so (pseudo):
timelinePolling = setInterval(function(e){
...
if ( array[end] <= timer ){
// [end] is always an odd-numbered 0-based indexed item
// in array list (see Note1 below).
timeline(array[start]);
// [start] is always an even-numbered 0-based indexed item
// in array list (see Note2 below).
// Note1: [end] must be swapped with next chronological
// odd-numbered indexed item in array list at this point in code.
/* Missing code here! */
// Note2: [start] must be swapped with next chronological
// even-numbered indexed item in array list at this point in code.
/* Missing code here! */
}
}}, 30);
So it's the dynamic index increment part that I wish to resolve, which would make my day to say the least. Perhaps my array should be constructed differently to facilitate the task? In this fashion the array must remain intact (no mutation). Any pointers/help will be appreciated.
Looks like you code in is in the right direction take a look at circular arrays:
https://www.geeksforgeeks.org/circular-array/
Possibly you are looking at something like this -
index = beg;
while (index < end) {
if( arr[index] < timer) {
// set beg and end appropriately
// swap the values you need from the notes
// Note1: [end] must be swapped with next chronological
// odd-numbered indexed item in array list at this point in code.
/* Missing code here! */
// Note2: [start] must be swapped with next chronological
// even-numbered indexed item in array list at this point in code.
/* Missing code here! */
timeline(arr[index+1]);
}
index += 2; // jump to the next even number.
index %= arr_len; //check here if there is something that needed to be done for even odd index
}
You could do this with setTimeout, and then you won't have to do the comparison yourself.
At the end of each timeout loop, you splice the array as you suggested, if there are still 3 or more entries in the array then recall the function that sets the timeout.
(The timer here is only to display the total time)
(I reduced your array of number to speed it up)
let timer = 0;
const times = ["4.30", "5.55", "6.25", "6.74", "5.75", "4.24"];
function test(array) {
const timeInMilliseconds = parseFloat(times[1]) * 1000;
setTimeout(() => {
document.querySelector('#ignore').innerHTML = array[0];
document.querySelector('#test').innerHTML = array[1];
document.querySelector('#display').innerHTML = array[2];
timer += timeInMilliseconds / 1000;
document.querySelector('#timer').innerHTML = timer;
document.querySelector('#array').innerHTML = array;
array = array.splice(2);
if (array.length >= 3) {
test(array);
}
}, timeInMilliseconds);
}
test(times);
<p>ignore: <span id="ignore">-</span></p>
<p>test: <span id="test">-</span></p>
<p>display: <span id="display">-</span></p>
<p>Timer: <span id="timer"></span></p>
<p>Array: <span id="array"></span></p>

How to approach creating array of random numbers with no repeats

I’ve created a lotto number generator and am having trouble making sure no two sets of numbers come out the same. I want to first check that the next generated number doesn’t match a previous one in the array, if it does then generate a new number.
The code:
https://codesandbox.io/s/billowing-leaf-oqdt3?file=/index.html
You can do it step by step by replacing what you have line 46 by:
// Get a random number
var rnd = rand(high);
// If it is already in the array, get another
while (numList.includes(rnd)) {
rnd = rand(high);
}
// Now you know the value is unique, so you can add it to the list.
numList.unshift(rnd);
use a hash:
var numberHash = {}
var num1=generateNum(numberHash);
var num2=generateNum(numberHash);
function generateNum(numberHash) {
var num = rand();
while(numberHash[num]) {
num = rand();
}
numberHash[num]=true;
return num;
}

I am trying to stop my function from displaying the same object twice when clicking a button

I have for quite some time now been trying to figure out how I can stop my code to print the same quote twice.
Also, when every single object in the array has been printed out, I'd like for it to reset somehow. So that you can browse through the quotes once you've gone through all of them.
This is the essential parts of my code:
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
The printQuote function simply contains information that's accessing information from my array:
var randomObjectNumber = getRandomQuote();
var html = "<p class='quote'>"
+ quotes[randomObjectNumber].quote +
"</p>";
document.getElementById('quote-box').innerHTML = html;
One random object is displayed each time you click the eventListener:
function getRandomQuote () {
var randomObjectNumber = Math.floor(Math.random() * quotes.length );
return randomObjectNumber;
}
I have some ideas on how to do this and I have tried them but without success. I tried giving each object a boolean property but I can't really seem to assign each property a boolean value without messing the printQuote function up.
I also tried assigning the object displayed to a different array but the same problem occurred there.
I feel like there is some concepts around the eventListener that I don't fully understand, because every time I try to manipulate a displayed object I just end up changing every single object.
This is what a typical object in the array looks like by the way:
{quote : "Darkness is merely the absence of light"}
(I also have other properties assigned to the object but i feel like presenting them would be redundant)
If someone could explain, or give me a hint, on how to solve this problem I've been struggling with for some time.
Some hints would be greatly appreciated!
Have a nice day.
Sebastian.
EDIT: All code: https://jsfiddle.net/fusqb7hz/
Basically what you need:
Create a separate array that will store all quotes that you've already used.
Remove quote from initial array.
Check if you still have quotes in initial array, if not, get them back from backup array.
The problem is that you call addEventListener twice:
//Let's developers create multiple eventListeners without being redundant.
function onClicking (printFunction) {
document.getElementById('loadQuote').addEventListener("click", printFunction, false);
}
onClicking(printColor);
onClicking(printQuote);
by calling onClicking twice you make the click happen twice, so addEventListener is added twice, meaning one click counts as two.
Change the above code for this:
//Let's developers create multiple eventListeners without being redundant.
document.getElementById('loadQuote').addEventListener("click", function(){
printColor();
printQuote();
});
Here is the jsfiddle:
https://jsfiddle.net/fusqb7hz/3/
I think the easiest approach is to shuffle your quote array and then go through them one by one. This gives you the next "random" as yet unseen quote. The only part I'm not keen on is this shuffler (a derivation of Fisher Yates) modifies the original quote array. You might not care about that though.
// --------------------------------
// A bunch of quotes
// --------------------------------
var quotes = [];
quotes.push({quote : "Darkness is merely the absence of light"});
quotes.push({quote : "quote 2"});
quotes.push({quote : "quote 3"});
quotes.push({quote : "quote 4"});
quotes.push({quote : "quote 5"});
// --------------------------------
// --------------------------------
// Your favorite array shuffle utility
// --------------------------------
var shuffle = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
// --------------------------------
// --------------------------------
// construct a function to get a random unseen quote until
// all quotes have been seen. Then reset...
// --------------------------------
var getQuote = (function(quotes, shuffle){
var current = 0;
var get = function(){
if ( !quotes || !quotes.length ) { return ""; }
if ( current >= quotes.length ){ current = 0; }
if ( current === 0 ){
console.log("randomizing quotes...");
shuffle(quotes);
}
return quotes[current++].quote;
};
return get;
})(quotes, shuffle);
// --------------------------------
var printQuote = function(){
document.getElementById('quote').innerText = getQuote();
};
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
<div id="quote"></div>
<button id="loadQuote">get quote</button>

JavaScript Higher Order Function loop/recursion/confusion

Implement a function that takes a function as its first argument, a number num as its second argument, then executes the passed in function num times.
function repeat(operation, num) {
var num_array = new Array(num);
for(var i = 0; i < num_array.length; i++){
return operation(num);
}
}
//
// The next lines are from a CLI, I did not make it.
//
// Do not remove the line below
module.exports = repeat
RESULTS:
ACTUAL EXPECTED
------ --------
"Called function 1 times." "Called function 1 times."
"" != "Called function 2 times."
null != ""
# FAIL
Why doesn't this work?
I am assuming that I am starting a function called repeat. Repeat has two parameters and takes two arguments.
For the loop I create an array which has a length which is equal to the num passed in.
I then start a for loop, setting a counter variable i to 0. Then I set a conditional which states that i should always be less than the length of the num_array which was created earlier. Then the counter i is incremented up by one using the ++.
For every time that the conditional is true, we should return the value of calling running the function operation and passing the num as an argument.
The last two lines allow for easy running of the program through command line with pre programmed arguments being used.
Thank you for your time!
The return statement is breaking out of the function on the first iteration of the loop. You need to remove the return, and just call the function like this:
function repeat(operation, num) {
for(var i = 0; i < num; i++){
operation(num);
}
}
Note that I have removed the creation and iteration of the array, you do not need it for what you are doing here.
Also your initial question does not specify that you need to pass num to the function (but you do list it in your steps below), so you may be able to just do operation() instead of operation(num).
You probably want something like the below, rather than returning the result of the function operation(num) you want to store the value in teh array. return in a loop breaks out of the loop, so it would always only run once..
function repeat(operation, num) {
var num_array = new Array(num);
for(var i = 0; i < num_array.length; i++){
num_array[i] = operation(num);
}
}
//
// The next lines are from a CLI, I did not make it.
//
// Do not remove the line below
module.exports = repeat
If you are asking why the loop is not running, it's because you have to run the function after defining it (I'm assuming you are not already calling the function somewhere else).
Once calling the function repeat you will see that it is exiting after one iteration. This is because you are returning the operation - returning causes the function to end. To stay in the loop you just need to call operation(), without the return.
Also you don't need to create an array, you can just use the counter you are defining in the for loop.
So the code would look something like this:
var op = function(arg) {console.log(arg);},
n = 5;
function repeat(operation, num) {
for(var i = 0; i < num; i++){
operation(i);
}
}
repeat(op ,n);
// The next lines are from a CLI, I did not make it.
//
// Do not remove the line below
module.exports = repeat

Give structure to 'random' function js?

I have an array and a function that picks randomly elements from this array and displays them in a div.
My array:
var testarray = [A, B, C, D, E, F];
Part of the js function:
var new_word = testarray[Math.floor((Math.random()*testarray.length)+1)];
$("#stimuli").text(new_word);
My question is, is there a way I can have them picked randomly in a certain ratio/order?
For example, that if I have my function executed 12 times, that each of the six letters is displayed exactly twice, and that there can never be the same letter displayed twice in a row?
You might want to try a quasi-random sequence. These sequences have the properties you're after. http://en.wikipedia.org/wiki/Low-discrepancy_sequence
Edit:
To your question in the comment: Of course there are hundreds ways to solve a problem. Think about using artificial intelligence, a mathematical algorithm or the answers given by others here. It depends on what you really want to achieve. I just gave a robust solution that is easy to understand and implement..
Here's another (different approach), same result but with the prevention that values displays twice in a row.
Jsfiddle: http://jsfiddle.net/kychan/jJE7F/
Code:
function StructuredRandom(arr, nDisplay)
{
// storage array.
this.mVar = [];
this.previous;
// add it in the storage.
for (var i in arr)
for (var j=0; j<nDisplay; j++)
this.mVar.push(arr[i]);
// shuffle it, making it 'random'.
for(var a, b, c = this.mVar.length; c; a = Math.floor(Math.random() * c), b = this.mVar[--c], this.mVar[c] = this.mVar[a], this.mVar[a] = b);
// call this when you want the next item.
this.next = function()
{
// default value if empty.
if (this.mVar.length==0) return 0;
// if this is the last element...
if (this.mVar.length==1)
{
// we must give it..
return this.mVar.pop();
// or give a default value,
// because we can't 'control' re-occuring values.
return -1;
}
// fetch next element.
var element = this.mVar.pop();
// check if this was already given before.
if (element==this.previous)
{
// put it on top if so.
this.mVar.unshift(element);
// call the function again for next number.
return this.next();
}
// set 'previous' for next call.
this.previous = element;
// give an element if not.
return element;
};
}
NOTE: In this example we can't fully control that the same values are displayed twice.. This is because we can control the first numbers, but when there is only one number left to display, we must either give it or display a default value for it, thus there is a chance that the same value is shown.
Good luck!
Like this?
var arr = [1,2,3,4,5,6,7], // array with random values.
maxDispl = 2, // max display.
arr2 = init(arr) // storage.
;
// create object of given array.
function init(arr)
{
var pop = [];
for (var i in arr)
{
pop.push({value:arr[i], displayed:0});
}
return pop;
}
// show random number using global var arr2.
function showRandom()
{
// return if all numbers has been given.
if (arr2.length<1) return;
var randIndex= Math.floor(Math.random()*arr2.length);
if (arr2[randIndex].displayed<maxDispl)
{
document.getElementById('show').innerHTML+=arr2[randIndex].value + ', ';
arr2[randIndex].displayed++;
}
else
{
// remove from temp array.
arr2.splice(randIndex, 1);
// search for a new random.
showRandom();
}
}
// iterate the function *maxDispl plus random.
var length = (arr.length*maxDispl) + 2;
for (var i=0; i<length; i++)
{
showRandom();
}
jsfiddle: http://jsfiddle.net/kychan/JfV77/3/

Categories