I am a javascript beginner and need your help! Thanks a lot for any help!
cell_660 = cell_638;
cell_659 = cell_637;
...
It worked that way. I now want to do this every second for all 660 variables to change their value to the value of the variable which name has the number (own number -22) at the end. Of course without writing 660 lines of code! I tried this so far:
var count = 660;
setInterval(function(){
for(var i=1;i<=660;i++){
'cell_' + count = 'cell_' + eval(count - 22);
count--;
}
count=660;
},1000);
How do I write this correctly? I already read about window['cell_' + count] - but I don't want to create 660 new variables every second. I want to CHANGE the value of 660 Variables every second.
Do you need to change 660 variables every second? What if you just create one numeric variable which keeps the location of "where in the array to start" when you display or use the array values?
var cells = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
var currentStart = 0;
// create an interval to modify currentStart
setInterval(function () {
currentStart = (currentStart + 22) % cells.length;
}, 1000);
function doSomething() {
// display contents starting at currentStart
var msg = "";
for (var i = 0; i < cells.length; i++) {
var index = (i + currentStart) % cells.length;
// do something with cells[index]
msg += "," + cells[index];
}
console.log(msg);
}
document.querySelector("#thing").addEventListener("click", doSomething);
<button id='thing'>click me</button>
with an array
how about having an array instead of this many variables ?
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
function moveArrElem() {
// loop all elements of the array
// using arr.map give us a buffer so current index are keeped while doing the modification
arr = arr.map((_, i) => {
// get the new index and make it valid if necessary
let newIndex = i - 2;
if (newIndex < 0) {
newIndex += arr.length
}
return arr[newIndex]
})
console.log(arr)
}
console.log(arr)
setInterval(moveArrElem, 2000)
about window['cell_' + count] it doesn't create a new variable each time, in fact var test = 42 is equal to window["test"] = 42 if you're in the global scope (outside of any functions and other blocks)
so window['cell_' + count] will just modify the value of the var
var test = 42
console.log(window["test"])
finally try to avoid eval as much as you can, it is slow and generally a security issue
some (I'm of these) a call to eval is almost always sign ther is a misconception elsewhere
without array
since OP told in comment he still hasn't learned array, here's a solution without array
let quantity = 660
// declaring the 660 vars
for (let i = 0; i < quantity; i++) {
window["cell_" + i] = i;
}
function moveCells() {
// need some tmp vars
for (let i = 0; i < quantity; i++) {
window["tmp_" + i] = window["cell_" + i];
}
for (let i = 0; i < quantity; i++) {
let newIndex = i - 22
if (newIndex < 0) {
// equals to : newIndex = newIndex + quantity
newIndex += quantity
}
window["cell_" + newIndex] = window["tmp_" + i];
}
// displaying 660 items would take to much time and place
console.log("cell_42 : " + cell_42)
console.log("cell_43 : " + cell_43)
console.log("cell_44 : " + cell_44)
console.log("cell_45 : " + cell_45)
console.log("cell_46 : " + cell_46)
}
console.log("cell_42 : " + cell_42)
console.log("cell_43 : " + cell_43)
console.log("cell_44 : " + cell_44)
console.log("cell_45 : " + cell_45)
console.log("cell_46 : " + cell_46)
setInterval(moveCells, 1000)
Before I give you an answer, I'd first like to point out this is terrible code practice. You should never need 660 variables! Whatever you're trying to do could probably be accomplished with arrays. That being said, here is how you would make this work, and how you should actually do this below. I strongly recommend that you consider going with the array method!
setInterval(function() {
for(var i = 660; i > 22; i--) {
window['cell_' + i] = window['cell_' + (i - 22)];
}
},1000);
Now here is something more along the lines of what you should actually be doing.
var cells = [/* 660 elements */];
setTimeout(function() {
for(var i = 22; i < 660; i++) {
cells[i] = cells[i - 22];
}
}, 1000);
In fact, with this method, you can do the whole thing in one line with the new Array#copyWithin method in ES6.
const cells = [/* 660 elements */];
setTimeout(() => {
cells.copyWithin(22);
}, 1000);
Related
I have an array that is constantly being updated, and needs to display the items in the array 5 at a time. Sometimes there are more than 5 elements in the array, sometimes there are less. If there are more than 5 elements in the array, then I need to cycle them 5 at a time. For example, if there are 10 elements, I want to fade in 1-5, then fade out 1-5, then fade in 5-10. I have this working, and updating, however, if there are only 4 news articles available after the data update, it still fades in and out 1-4, over and over. I need to always fade in the first articles, and if there are less than the numberToShow, don't fade out, just update.
I have tried clearInterval, but that stops updating. I tried .stop().fadeOut(); but then the fade in keeps occurring. I tried .stop().fadeOut(); with .stop().fadeIn(); but the data never fades in. Should I pass the array in to display it, and cycle in there?
For testing, this is simulated with using the date. Every 8 seconds it should update the the data with an updated number. If there are 4 articles, fade in, and update the Date.now() number, but never fade out. If there are 10 articles, fade in and update each cycle.
var numberToShow = 5;
var newsArray = [];
var startRow = 0;
var endRow = 0;
function getData() {
// Simulate the data changing using date.
newsArray = [Date.now(), "News article 1", "News article 2", "News article 3", "News article 4",
"News article 5", "News article 6", "News article 7", "News article 8", "News article 9"];
showNews(numberToShow);
}
// Fade out the results for the next cycle
setInterval(function() {
$("span.text").fadeOut({
duration: 800
});
setTimeout(
function() {
getData();
},
(800)
);
}, 8000);
// Update the data
function updateData() {
getData();
setTimeout(updateData, 6000);
}
// Display the results
function showNews() {
if (endRow >= newsArray.length) {
startRow = 0;
}
endRow = startRow + numberToShow;
if (endRow >= newsArray.length) {
endRow = newsArray.length;
}
var results = "";
for (var k = startRow; k < endRow; k++) {
results += "<span class='text' style='display:none;'>" + newsArray[k] + "</span><br>";
}
startRow = startRow + numberToShow;
document.getElementById('showResults').innerHTML = results;
$("span.text").fadeIn({
duration: 800
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="showResults"></div>
While previous answer works fine - this one could be more like the case in your description...
Short description of the idea:
show list or the part of it.
if list is longer - repeat (go to step 1 in a couple of secs to show another part of the list)
when update comes - anytime - start again with new array
And working example (removed unneeded code and added button to help with tests):
var
numberToShow = 5,
newsArray = [],
startRow = 0,
endRow = 0,
$results = $("#showResults"),
timer;
function getData() {
// Simulate the data changing
newsArray = [Date.now()];
// add random number of items
var j = Math.floor(Math.random()*7)+1;
for(var i=0; i<j; i++){
newsArray.push('News article '+i);
}
// add one more item named "last"
newsArray.push('Last News article');
startCycle();
}
function startCycle() {
startRow = 0;
endRow = 0;
$results.fadeOut(800, function(){
renderList();
});
}
function renderList() {
if (endRow >= newsArray.length) {
startRow = 0;
}
endRow = startRow + numberToShow;
if (endRow > newsArray.length) {
endRow = newsArray.length;
}
var results = "";
for (var k = startRow; k < endRow; k++) {
results += "<span class='text'>" + newsArray[k] + "</span><br>";
}
startRow = startRow + numberToShow;
$results.html(results);
$results.fadeIn(800, function(){
nextCycle();
});
}
function nextCycle() {
// start cycling only if there is more results to be shown
if(newsArray.length > numberToShow){
timer = setTimeout(function(){
$results.fadeOut(800, function(){
renderList();
});
}, 4000);
}
}
// update on request
function updateData() {
clearTimeout(timer);
$results.stop();
getData();
}
// add button for tests
$results.before(
$('<button/>').text('Update now').click(function(){
updateData();
})
)
getData();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="showResults"></div>
Ok - not sure what exactly you tried to do - but you will easily change my code into anything you need
...lets start with clear algorithm:
getData
fade out if list is visible ...then continue to step 3.
render next portion of items from array
fade in if list is not visible ...then go to step 5.
wait a while...
check if all items has been shown (if not - show next portion with step 2, if so - update data with step 1)
hope the the code will give you a chance to adopt it to your needs:
var
numberToShow = 5,
newsArray = [],
startRow = 0,
endRow = 0,
$results = $("#showResults"),
visible = false,
timer;
function fadeInIfNeeded(callback) {
var is_visible = visible;
visible = true;
if(is_visible){
callback();
}else{
$results.fadeIn(800, callback);
}
}
function fadeOutIfNeeded(callback) {
var is_visible = visible;
visible = false;
if(is_visible){
$results.fadeOut(800, callback);
}else{
callback();
}
}
function getData() {
// Simulate the data changing
newsArray = [Date.now()];
// add random number of items
var j = Math.floor(Math.random()*6)+2;
for(var i=1; i<j; i++){
newsArray.push('News article '+i);
}
// add one more item named "last"
newsArray.push('Last News article');
startCycle();
}
function startCycle() {
startRow = 0;
endRow = 0;
fadeOutIfNeeded(function(){
renderList();
});
}
function renderList() {
if (endRow >= newsArray.length) {
startRow = 0;
}
endRow = startRow + numberToShow;
if (endRow > newsArray.length) {
endRow = newsArray.length;
}
var results = "";
for (var k = startRow; k < endRow; k++) {
results += "<span class='text'>" + newsArray[k] + "</span><br>";
}
startRow = startRow + numberToShow;
$results.html(results);
fadeInIfNeeded(function(){
nextCycle();
});
}
function nextCycle() {
// every portion of data will be seen for 6 + 0.8 + 0.8 = 7.6 sec
timer = setTimeout(function(){
if(startRow >= newsArray.length){
// if all items has been shown - get new data (update)
getData();
}else{
// if there is more to show - fade out and render
fadeOutIfNeeded(function(){
renderList();
});
}
}, 6000);
}
getData();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="showResults"></div>
Here is a modern view of how to implement your use case. Please note there
is not a single global variable and all DOM changes (side effects) occur in a single function.
You describe a constantly changing array. This code produces that array of integers
with one remarkable difference. After the data is set/retrieved, a Custom Event
is produced and fired. That event carries the changed/updated array.
function newData(data) { // Random size of array (between 1-15)
const previousLength = data.length;
// ~50% of the time the array grows, otherwise it shrinks
data.length = (Math.random() > .5)?
data.length + Math.ceil(Math.random() * (7 - 1) + 1) :
Math.ceil(Math.random() * (4 - 1) + 1);
for (let i = previousLength; i < data.length; i++) {
data[i] = Math.ceil(Math.random() * (15 - 1) + 1);
}
let dataEvent = new CustomEvent('gotData', { detail: data});
document.getElementById('showResults').dispatchEvent(dataEvent);
}
Your question describes what is the need for a custom iterator that provides no more than 5
array elements for each iteration. The following code provides a Generator function that follows
JavaScript's iterator protocol by returning a result with a .value property
containing an array of no more than 5 elements and a .done property containing a Boolean indicating if there
is no futher data. The yield statement returns data or the return statement results in
.done being set to true to indicate there is no further data. (see the MDN articles for details)
function* nextSet(data = [], numberToShow = 5) {
let current = [];
let currentStart = 0;
while (true) {
[current] = [data.slice(currentStart, currentStart + numberToShow)];
if (currentStart < data.length) {
yield current;
} else {
return;
}
currentStart += numberToShow;
}
}
With the data and iterator in place this code starts off the procession. Set up
an event listener for the custom event, then get some mock data (starting with an
empty array):
document.getElementById('showResults').addEventListener('gotData', doDOM);
newData([]);
All the DOM work is done in the event callback function below (doDOM()).
First create an iterator from the Generator Function.
Then start the interval timer so that we can repeatedly call .next() on the iterator.
Please note how dead-simple the animation actually is with
a bit of rethinking the approach to the entire problem. If result is undefined
then cancel the interval timer, mock more data and repeat the process with the updated array.
function doDOM(event) {
const data = event.detail;
const iterator = nextSet(data); // create iterator from Generator
let text = '';
let page = 0;
let interval = setInterval(()=>{
page++;
let result = iterator.next().value;
if(result) {
text = `Array size: ${data.length} (Page ${page}) -- ${JSON.stringify(result)}`;
// Dead simple animations...
$(event.target).fadeOut(1000, () => {
event.target.innerText = text;
$(event.target).fadeIn(1000);
});
} else {
event.target.innerText += " ----> getting more data..."
// all done, so kill this one
clearInterval(interval);
// Mock new data arrival
newData(data);
return;
}
}, 5000);
}
I do realize this seems to be a mile off from your question. But this answer addresses the whole
puzzle rather than just one bit.
/**
* A Generator function to produce number of data elements
*/
function* nextSet(data = [], numberToShow = 5) {
let current = [];
let currentStart = 0;
while (true) {
[current] = [data.slice(currentStart, currentStart + numberToShow)];
if (currentStart < data.length) {
yield current;
} else {
return;
}
currentStart += numberToShow;
}
}
/**
* CustomEvent Handler - fired when new data is received
*
* DOM manipulations
* All side effects are contained within one function
* #parm Event - contains detail with data
*/
function doDOM(event) {
const data = event.detail;
const iterator = nextSet(data); // create iterator from Generator
let text = '';
let page = 0;
let interval = setInterval(() => {
page++;
let result = iterator.next().value;
if (result) {
text = `Array size: ${data.length} (Page ${page}) -- ${JSON.stringify(result)}`;
// Dead simple animations...
$(event.target).fadeOut(1000, () => {
event.target.innerText = text;
$(event.target).fadeIn(1000);
});
} else {
event.target.innerText += " ----> That's it! Getting more data..."
// all done, so kill this one
clearInterval(interval);
// Mock new data arrival
newData(data);
return;
}
}, 5000);
}
// Array that is either growing or changing on each call
function newData(data) { // Random size of array (between 1-15)
const previousLength = data.length;
// ~50% of the time the array grows, otherwise it shrinks
data.length = (Math.random() > .5) ?
data.length + Math.ceil(Math.random() * (7 - 1) + 1) :
Math.ceil(Math.random() * (4 - 1) + 1);
for (let i = previousLength; i < data.length; i++) {
data[i] = Math.ceil(Math.random() * (15 - 1) + 1);
}
let dataEvent = new CustomEvent('gotData', {
detail: data
});
document.getElementById('showResults').dispatchEvent(dataEvent);
}
document.getElementById('showResults').addEventListener('gotData', doDOM);
newData([]);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script defer src="index.js"></script>
</head>
<body>
<main id="showResults">Welcome! ----> waiting for data...</main>
</body>
</html>
Expand the code snippet to see this work.
I created two functions, GOODself and BADself, called in succession to debug a problem I have in a larger script illustrated here. I can't figure out why BADself stalls at the line aryVarAbi[0] = 5;.
If I call it first it still happens. According to the console in Firefox aryVarAbi is not defined.
GAB = GOODself(0, 4, 1);
GAB = BADself(0, 4, 1);
function GOODself(GABin, nCols, nRows) {
var aCol = 0;
var aryVarABi = [1,1,1,1];
for (aCol=0; aCol < nCols - 1; aCol++) {
alert("GOOD1 " + aryVarABi[aCol]);
aryVarABi[0]= 5;
alert("GOOD2 " + aryVarABi[aCol]);
} // for aCol
return (aryVarABi[0]);
} // GOODself
function BADself(GABin, nCols, nRows) {
var aCol = 0;
var aryVarABi = [1,1,1,1];
for (aCol=0; aCol < nCols - 1; aCol++) {
alert("BAD1 " + aryVarABi[aCol]);
aryVarAbi[0] = 5;
alert("BAD2 " + aryVarABi[aCol]);
} // for aCol
return (aryVarABi[0]);
} // BADself
JavaScript is case senstive. You've defined var aryVarABi = [1,1,1,1];. Notice the capital B. You're referring to aryVarAbi[0] = 5;. Notice the lowercase b.
I think you want aryVarABi[0] = 5; as opposed to aryVarAbi[0] = 5;
The 'b' in the latter is lowercase.
Javascript variables are case sensitive, so aryVarABi is a different variable from aryVarAbi.
Please change the line aryVarAbi[0] = 5; to aryVarABi[0] = 5;.
I have an array which is taking values over the space of a second and calculating the average. The array is then permanently storing the values. I need them to remove the values after the average has been calculated. I have tried clearing the array at the end of the calculateAverage function using levelMeasurements = []; and levelMeasurements = 0; but that does not work.
Any help on this would be greatly appreciated, thanks!
My code:
var levelMeasurements = [];
function collectLevelMeasurements() {
levelMeasurements.push(averagingLevel);
}
var collectInterval = window.setInterval(collectLevelMeasurements, 0);
function calculateAverage() {
window.clearInterval(collectInterval);
var avg = 0;
for (counter = 0; counter < levelMeasurements.length; counter++) {
avg += levelMeasurements[counter] / levelMeasurements.length;
}
averageAbsoluteLevel = Math.abs(avg);
averageDbLevel = Tone.gainToDb(averageAbsoluteLevel) * scale + offset;
console.log("Measure for 5 minutes: average level is:" + averageDbLevel);
}
window.setInterval(calculateAverage, 1000);
Your collectInterval timeout is never reinstated after the first time calculateAverage is called, unless the issue is something else not visible from the code.
In your code calculateAverage is called every 1000 milliseconds, but the values stored in levelMeasurements are not updated after the first window.clearInterval.
I made a simplified snippet without Tone lib:
var levelMeasurements = [];
//added this as a easy source of values
let count = 0;
function collectLevelMeasurements() {
// levelMeasurements.push(averagingLevel);
levelMeasurements.push(count++);
}
var collectInterval = window.setInterval(collectLevelMeasurements, 0);
function calculateAverage() {
window.clearInterval(collectInterval);
var avg = 0;
for (counter = 0; counter < levelMeasurements.length; counter++) {
avg += levelMeasurements[counter] / levelMeasurements.length;
}
averageAbsoluteLevel = Math.abs(avg);
averageDbLevel = averageAbsoluteLevel;
//prolly dont need tone lib for this example
// averageDbLevel = Tone.gainToDb(averageAbsoluteLevel) * scale + offset;
console.log("Measure for 5 minutes: average level is:" + averageDbLevel);
//reset these variables
levelMeasurements = [];
count = 0;
console.log(levelMeasurements)//empty array at this point
//reset the collectInterval!
collectInterval = window.setInterval(collectLevelMeasurements, 0);
}
window.setInterval(calculateAverage, 1000);
You can try following:
levelMeasurements.length = 0.
Or refer more solution at here: How do I empty an array in JavaScript?
Let's say I want a variable to contain numbers from 1 to 100.
I could do it like this:
var numbers = [1,2,3,4,...,98,99,100]
But it would take a bunch of time to write all those numbers down.
Is there any way to set a range to that variable? Something like:
var numbers = [from 1, to 100]
This might sound like a really nooby question but haven't been able to figure it out myself. Thanks in advance.
Store the minimum and maximum range in an object:
var a = {
from: 0,
to: 100
};
In addition to this answer, here are some ways to do it:
for loop:
var numbers = []
for (var i = 1; i <= 100; i++) {
numbers.push(i)
}
Array.prototype.fill + Array.prototype.map
var numbers = Array(100).fill().map(function(v, i) { return i + 1; })
Or, if you are allowed to use arrow functions:
var numbers = Array(100).fill().map((v, i) => i + 1)
Or, if you are allowed to use the spread operator:
var numbers = [...Array(100)].map((v, i) => i + 1)
However, note that using the for loop is the fastest.
You can easily create your own, and store only the limits:
function Range(begin, end) {
this.low = begin;
this.hi = end;
this.has = function(n) {
return this.low <= n <= this.hi;
}
}
// code example
var range = new Range(1,100);
var num = 5;
if (range.has(num)) {
alert("Number in range");
}
Supported in all modern browsers including IE9+.
var numbers = Array.apply(null,Array(100)).map(function(e,i){return i+1;});
For what it's worth, #MaxZoom had answer that worked for my situation. However, I did need to modify the return statement to evaluate with && comparison. Otherwise appeared to return true for any number.
function Range(begin, end) {
this.low = begin;
this.hi = end;
this.has = function(n) {
//return this.low <= n <= this.hi;
return ( n >= this.low && n <= this.hi );
};
}
// code example
var alertRange = new Range(0,100);
var warnRange = new Range(101, 200);
var num = 1000;
if (alertRange.has(num)) {
alert("Number in ALERT range");
//Add Alert Class
}
else if (warnRange.has(num)) {
alert("Number in WARN range");
//Add Warn Class
}
else{
alert("Number in GOOD range")
//Default Class
}
Python
# There can be two ways to generate a range in python
# 1)
a = [*range(5)]
print(a)
#[0, 1, 2, 3, 4]
# 2)
a = [*range(5,10)]
print(a)
#[5, 6, 7, 8, 9]
Javascript
// Similar pattern in Javascript can be achieved by
let a;
// 1)
a = [...Array(5).keys()]
console.log(a) //[0, 1, 2, 3, 4]
// 2)
a = [...Array(5).keys()].map(value => value + 5);
console.log(a) //[5,6,7,8,9]
I am trying to dynamically set the css style of some divs which are listed. When I iterate over them, and set their new css values, only the last div will get the new css values.
for (var i = 0, l = list.length; i < l; i++)
{
b = list[i];
b.$block.css({
"top": b.pos.y + "px",
"left": b.pos.x + "px"
});
b.pos = b.pos.add(b.vec);
}
b.$block is the jQuery object. b.pos and b.vec are just some Vector2D instances which I use to calculate the top and left value all divs.
The css() method does affect all the jQuery objects in the collection, but if you run your code in a debugger, you can see that you are passing the same object every time: div#block3.
Your problem lies elsewhere outside of the blockSim(list) method because you are passing a list of the same object.
New solution
You forgot 3 times the keyword new:
return (Vector2D(that.x + other.x, that.y + other.y)); // line 92
return (Vector2D(that.x - other.x, that.y - other.y)); // line 96
metaList.push(createBlock(i)); // line 133
You have to write
return (new Vector2D(that.x + other.x, that.y + other.y)); // line 92
return (new Vector2D(that.x - other.x, that.y - other.y)); // line 96
metaList.push(new createBlock(i)); // line 133
Just with the new keyword the this parameter of the functions is set to a new value. Here the code: http://jsfiddle.net/6eB3U/1/
Old solution
Here you have a debugged example of your code: http://jsfiddle.net/8ET77/
Consider the function createBlock():
function createBlock(idx)
{
var tmp = new Array(4);
for (var i = 0; i < 4; i++) {
if (i < 2)
tmp[i] = Math.round(Math.random() * 1000) % 600;
else
tmp[i] = Math.round(Math.random() * 10) % 4;
}
this.pos = new Vector2D(tmp[0], tmp[1]);
this.vec = new Vector2D(tmp[2], tmp[3]);
this.$block = $("#block"+idx);
return this;
}
For each call on the function, the reference this points to the same object (i.e. the function object createBlock()). The corrected function now looks like this:
function createBlock(idx)
{
var tmp = new Array(4);
for (var i = 0; i < 4; i++) {
if (i < 2)
tmp[i] = Math.round(Math.random() * 1000) % 600;
else
tmp[i] = Math.round(Math.random() * 10) % 4;
}
block = {}; // new object is generated here!
block.pos = new Vector2D(tmp[0], tmp[1]);
block.vec = new Vector2D(tmp[2], tmp[3]);
block.$block = $("#block"+idx);
return block;
}
You have to make the same correction for the function Vector2D().
Can you try to use jQuery $.each function? It is probably solve your problem.
It just may something like that.
$.each(b.$block, function( index, value ) {
$(value).css({"top" , b.pos.x + "px"});
});