Display letters one after the other for a looped sequence - javascript

I want to display multiple strings one after the other and I want the letters in each string to appear one at a time until the string is complete and it loops to the next string. Should run on a continuous loop.
var example = ['IT Solutions', 'Professional Work Ethic'];
textSequence(0);
function textSequence(i) {
if (example.length > i) {
setTimeout(function() {
document.getElementById("sequence").innerHTML = example[i];
textSequence(++i);
}, 4000);
} else if (example.length == i) { // Loop
textSequence(0);
}
}
<div class="container" id="sequence"></div>

I prefer #adpro's answer, but here's an alternative that keeps your original array:
showLettersOf(
['IT Solutions', 'Professional Work Ethic'],
document.querySelector('#sequence')
);
function showLettersOf(arrayOfStrings, el) {
var stringIndex=0, letterIndex=0, str="";
return setInterval(function(){
str += arrayOfStrings[stringIndex].charAt(letterIndex++);
el.innerHTML = str;
if (letterIndex >= arrayOfStrings[stringIndex].length){
letterIndex=0;
str="";
if (++stringIndex >= arrayOfStrings.length) stringIndex=0;
}
}, 100);
}

You can join the strings, and then loop over them almost the same as you are. But you'll want to hold on to the value and pass it in recursively to keep track of what you have already.
This may not be exactly what you want, but I think you'll be able to figure it out by using this.
var example = ['IT Solutions', 'Professional Work Ethic'];
var eString = example.join(' ');
textSequence(0, '');
function textSequence(i, value) {
if (eString.length > i) {
setTimeout(function() {
value += eString[i];
document.getElementById("sequence").innerHTML = value;
textSequence(++i, value);
}, 100);
} else if (eString.length == i) { // Loop
textSequence(0, '');
}
}
<div class="container" id="sequence"></div>

Related

Can't we reassign array values inside forEach?

The problem statement is, i should replace the any digit below 5 with 0 and any digit 5 and above with 1.
I am trying to reassign values, but it is not affecting, Why?
function fakeBinary(n) {
let numbersArr = n.split('');
numbersArr.forEach(num => {
if(Number(num) < 5) {
num = '0';
} else if(Number(num) >= 5) {
num = '1';
}
});
return numbersArr.join('');
}
console.log(fakeBinary('3457'));
I except the output of 0011, but the actual output is 3457.
forEach used like that won't do anything - use map instead.
let numbersArr = n.split("").map(num => {
if (Number(num) > 5) {
num = "0";
} else if (Number(num) <= 5) {
num = "1";
}
return num;
});
return numbersArr.join("");
Note that to produce your desired output, you need to change your conditions slightly:
if (Number(num) >= 5) {
num = "1";
} else {
num = "0";
}
forEach doesn't bring the element's reference for primitive values but rather brings a copy of the value in your case. You can easily access that manually through the index, though:
function fakeBinary(n) {
let numbersArr = n.split('');
numbersArr.forEach((num, i) => {
// ^--- note that `i` is brought and used below to access the element at index [i].
if(Number(num) < 5) {
numbersArr[i] = '0';
} else if(Number(num) >= 5) {
numbersArr[i] = '1';
}
});
return numbersArr.join('');
}
console.log(fakeBinary('3457'));
Please note that you may also use other prototypes, I just tried to stay as close as possible to your solution, you may also want to use map or, even (not appropriate, though) reduce or even a regular for loop.
To do it with forEach, you would need to use the additional arguments to reference the array and the index.
function fakeBinary(n) {
let numbersArr = n.split('');
numbersArr.forEach((num, index, arr) => {
if(Number(num) < 5) {
arr[index] = '0';
} else if(Number(num) >= 5) {
arr[index] = '1';
}
});
return numbersArr.join('');
}
console.log(fakeBinary('3457'));
But forEach is not the correct way to return a new array. You want to use map()
function fakeBinary(n) {
return n
.split('')
.map(num => (Number(num) < 5) ? '0' : '1')
.join('');
}
console.log(fakeBinary('3457'));
A shorter version using Array.from()
const fakeBinary = (str) => Array.from(str, n => +(+n >= 5)).join('');
console.log(fakeBinary('3457'));

Reverse order of an array and return the size of it javascript

I have a strange problem. I have to implement a function count(s) which inverts the getNumberSequence function that I have already create. (i.e: count(getNumberSequence(x)) == x, for all integers x > 0). I have my function and I have also the logic to resolve the problem but I don't know how to do it. In my case I want to call the previous string of ordered numbers, split them and call the last number. The problem is, how can I call the return of another method? Here are my codes:
function getNumberSequence(number) {
var result = "";
if (number <= 0) {
return result;
} else {
var first = true;
for (i = 1; i <= number; i++) {
if (first) {
first = false;
} else {
result += ", ";
}
result += i;
}
}
return result
}
Basically I need the variable result in this case to call it in the other function.
function count(s) {
var d = s. split(', ');
return d[-1];
}
I know that the second code is wrong but I don't know how to fix it. I have implemented a test that is:
test( "Count", function() {
for (i = 1; i<10000; i = i + 10) {
equal(count(getNumberSequence(i)) , i, "count(getNumberSequence(" +i + ")) should return " + i);
}
I know that the answer could be stupid but I started javascript yesterday for the first time. Hope you can help me. Thanks to everyone
If I understand you correctly you want to pass a number, say 10, into the first function and have that return a string of numbers up to 10 and then for count to read that string and return 10 (as an integer) again? This will do the trick. It takes the string, splits it, and pops out the last number converting it to an integer before it returns it.
function count(seq) {
return parseInt(seq.split(', ').pop(), 10);
}
I could rewrite it like this:
function count(seq) {
// create an array
var arr = seq.split(', ');
// grab the last element (a string)
var lastElement = arr.pop();
// convert the string to an integer
var convertedInteger = parseInt(lastElement, 10);
// return the integer
return convertedInteger;
}
If you wanted to use reverse and grab the first element, do this:
function count(seq) {
return parseInt(seq.split(', ').reverse()[0], 10);
}
Or use shift which does the same thing:
function count(seq) {
return parseInt(seq.split(', ').reverse().shift(), 10);
}
DEMO

Executing JavaScript in Order

I'm trying to print to a div one character at a time. It works, however, it runs both lines at the same time so that all I get is a jumbled mess.
How can I make the commands run one after the other?
function print(str){
var arr = str.split("");
var i = 0;
function write(){
setTimeout(function(){
if(i < arr.length){
var cont = $(".content").html();
cont = cont.replace("_","");
$(".content").html(cont + arr[i] + "_");
i++;
write();
}
},30);
}
write();
}
var str = [
"I am the egg man",
"I am the walrus"
];
for(x in str){
print(str[x];
}
jsFiddle: http://jsfiddle.net/PscNC/1/
You have two asynchronous functions that you start one right after the other so they run in parallel. If you want them to run serially, then you have to create some sort of notification when the first one is done so you then can trigger the start of the next one and so on. This can be done a number of ways (I show three ways below). You can use a callback, you can use promises and you can avoid having to sequence the async operations at all.
Method #1 - Completion Callback
Here's adding a callback to your print function and then use that to trigger the next string to go and then changing your iteration of strings to use the callback:
Working demo: http://jsfiddle.net/jfriend00/Lyu5V/
$(function() {
function print(str, fn) {
var i = 0;
var items = $(".content");
function write() {
setTimeout(function() {
if (i < str.length) {
items.html(items.html().replace("_", "") + str.charAt(i) + "_");
i++;
write();
} else {
fn();
}
}, 100);
}
write();
}
var data = [
"I am the egg man...",
"I am the walrus"
];
var i = 0;
function next() {
if (i < data.length) {
print(data[i++], next);
}
}
next();
});
FYI, there's really no reason to split your string into an array. You can access the individual characters of the string with the .charAt(index) method.
Method #2 - Promises - use .then() to sequence operations
And, here's a version of your code using promises instead of passing the callback:
Working demo: http://jsfiddle.net/jfriend00/97UtX/
$(function() {
function print(str) {
var i = 0, items = $(".content"), def = $.Deferred();
function write() {
setTimeout(function() {
if (i < str.length) {
items.html(items.html().replace("_", "") + str.charAt(i) + "_");
i++;
write();
} else {
def.resolve();
}
}, 100);
}
write();
return def.promise();
}
var data = [
"I am the egg man..",
"I am the walrus"
];
data.reduce(function(p, item) {
return p.then(function() {
return print(item);
});
}, $.Deferred().resolve());
});
Method #3 - Avoid sequencing by combining data into one single operation
And, if you want to simplify/DRY it up a bit, you can do this which avoids having to sequence the successive operations by just turning it into one longer operation and I made a few simplifications to your code:
Working demo: http://jsfiddle.net/jfriend00/TL8pP/
$(function() {
function print(str) {
var i = 0, items = $(".content");
function write() {
setTimeout(function() {
if (i < str.length) {
items.html(items.html().replace("_", "") + str.charAt(i) + "_");
i++;
write();
}
}, 100);
}
write();
}
var data = [
"I am the egg man..",
"I am the walrus"
];
print(data.join(""));
});
This is based on jfriend's answer, but it uses primitives with promises rather than promises at a high level. I believe this makes for cleaner code.
First, let's write a function that represents a delay with promises:
function delay(ms){ // generic delay function
var d = $.Deferred();
setTimeout(d.resolve, ms);
return d;
}
Next, let's use promises to their fullest
var delay100 = delay.bind(null, 100); // a 100 ms delay
function write(el, str, initial) { // write a single word
return [].reduce.call(str, function (prev, cur) { // reduce is generic
return prev.then(delay100).then(function (letter) {
initial += cur;
el.text(initial + "_");
});
}, $.when());
}
data.reduce(function (p, item) {
return p.then(function () { // when the last action is done, write the next
return write($(".content"), item, ""); // might want to cache this
});
}, $.ready.promise()); // we don't need `$(function(){})`
Here is a fiddle illustrating this solution: http://jsfiddle.net/feq89/
Just for fun, here is an ES6 solution without jQuery:
var delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
var write = (el, str, initial) =>
[].reduce.call(str, (prev, cur) =>
prev.then(() => delay(100)).then(() => {
initial += cur;
el.textContent = initial + "_";
});
}, Promise.resolve());
var content = document.querySelector(".content");
data.reduce((p, item) => p.then(() => write(content, item, "")));
bobef is right.
Add another argument to print, which is a callback.
And you should call the print method inside another recursive method instead a loop.
function print(str, _cb){
var arr = str.split("");
var i = 0;
function write(){
setTimeout(function(){
if(i < arr.length){
var cont = $(".content").html();
cont = cont.replace("_","");
$(".content").html(cont + arr[i] + "_");
i++;
write();
} else {
_cb();
}
},30);
}
write();
}
var str = [
"I am the egg man",
"I am the walrus"
];
var j = 0,
callback = function () {
if(j < str.length){
print (str[j++], callback);
}
};
callback();

javascript integer to string error

Really sorry if you've gone over this already with me. The good news: I've made progress. The bad news: it's broken progress.
I have a function that counts up from a certain number. It's a big number, and I need to insert commas in the correct places. Here's the code I've put together. It works at first, but then displays NaN...
http://jsfiddle.net/blackessej/TT8BH/7/
function createCounter(elementId,start,end,totalTime,callback)
{
var jTarget=jQuery("#"+elementId);
var interval=totalTime/(end-start);
var intervalId;
var current=addCommas(start)+'';
var f=function(){
jTarget.text(current);
if(current==end)
{
clearInterval(intervalId);
if(callback)
{
callback();
}
}
++current;
}
intervalId=setInterval(f,interval);
f();
}
jQuery(document).ready(function(){
createCounter("counter",12714086,9999999999,10000000000000,function(){
alert("finished")
})
})
function addCommas(str) {
var amount = new String(str);
amount = amount.split("").reverse();
var output = "";
for ( var i = 0; i <= amount.length-1; i++ ){
output = amount[i] + output;
if ((i+1) % 3 == 0 && (amount.length-1) !== i)output = ',' + output;
}
return output;
}
The problem comes from adding the commas to the number. Just convert the number to the comma string before putting it in the .text() function:
http://jsfiddle.net/TT8BH/11/
When you call addCommas, you are returning a String, nicely formatted for humans, but terrible for things like ++. You need to track and modify the start variable, and only convert to string when necessary:
var f=function(){
jTarget.text(addCommas(start));
if(start==end)
{
clearInterval(intervalId);
if(callback)
{
callback();
}
}
++start;
}

Create a JavaScript loop without while

I am trying to create a page which needs to preform lots of loops. Using a while/for loops cause the page to hang until the loop completes and it is possible in this case that the loop could be running for hours. I have also tried using setTimeout, but that hits a recursion limit. How do I prevent the page from reaching a recursion limit?
var looper = {
characters: 'abcdefghijklmnopqrstuvwxyz',
current: [0],
target: '',
max: 25,
setHash: function(hash) {
this.target = hash;
this.max = this.characters.length;
},
getString: function() {
string = '';
for (letter in this.current) {
string += this.characters[this.current[letter]];
}
return string;
},
hash: function() {
return Sha1.hash(this.getString());
},
increment: function() {
this.current[0] += 1;
if (this.current[0] > this.max) {
if (this.current.length == 1) {
this.current = [0, 0];
} else {
this.current[1] += 1;
this.current[0] = 0;
}
}
if (this.current[1] > this.max) {
if (this.current.length == 2) {
this.current[2] == 0;
} else {
this.current[3] += 1;
this.current[2] = 0;
}
}
},
loop: function() {
if (this.hash() == this.target) {
alert(this.getString());
} else {
this.increment();
setTimeout(this.loop(), 1);
}
}
}
setInterval is the usual way, but you could also try web workers, which would be a more straightforward refactoring of your code than setInterval but would only work on HTML5 browsers.
http://dev.w3.org/html5/workers/
Your setTimeout is not doing what you think it's doing. Here's what it's doing:
It encounters this statement:
setTimeout(this.loop(), 1);
It evaluates the first argument to setTimeout, this.loop(). It calls loop right there; it does not wait for a millisecond as you likely expected.
It calls setTimeout like this:
setTimeout(undefined, 1);
In theory, anyway. In reality, the second step never completes; it recurses indefinitely. What you need to do is pass a reference to the function rather than the returned value of the function:
setTimeout(this.loop, 1);
However, then this will be window on the next loop, not looper. Bind it, instead:
setTimeout(this.loop.bind(this), 1);
setInterval might work. It calls a function every certain amount of milliseconds.
For Example
myInterval = setInterval(myFunction,5000);
That will call your function (myFunction) every 5 seconds.
why not have a loop checker using setInterval?
var loopWorking = false;
function looper(){
loopWorking = true;
//Do stuff
loopWorking = false;
}
function checkLooper()
{
if(loopWorking == false)
looper();
}
setInterval(checkLooper, 100); //every 100ms or lower. Can reduce down to 1ms
If you want to avoid recursion then don't call this.loop() from inside of this.loop(). Instead use window.setInterval() to call the loop repeatedly.
I had to hand-code continuation passing style in google-code prettify.
Basically I turned
for (var i = 0, n = arr.length; i < n; ++i) {
processItem(i);
}
done();
into
var i = 0, n = arr.length;
function work() {
var t0 = +new Date;
while (i < n) {
processItem(i);
++i;
if (new Date - t0 > 100) {
setTimeout(work, 250);
return;
}
}
done();
}
work();
which doesn't hit any recursion limit since there are no recursive function calls.

Categories