setTimeout within for loop not working [duplicate] - javascript

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
Using Javascript I am trying to take a string of text and have it broken down into individual words and then have them displayed consecutively separated by a set delay. I have looked at other 'set timeout in for loop' stack overflow answers but can't figure it out. The code below properly separates the string into an array of words but then loops to displaying the last word without delay.
var string = "Display this line of text as individual words in two second intervals!";
var delay = 2000;
function myFunction() {
var array = string.split(' ');
for (var i = 0; i < array.length; i++) {
setTimeout(function() {
document.getElementById("textbox").innerHTML = array[i];
}, delay);
}
}
<button onclick="myFunction()">Run</button>
<div id="textbox"></div>

You should use closures for this. Also, look at how the data is passed to the innerHTML.
var string = "Display this line of text as individual words in two second intervals!";
var delay = 2000;
function myFunction() {
var array = string.split(' ');
for (var i = 0; i < array.length; i++) {
(function (str) {
setTimeout(function () {
document.getElementById("textbox").innerHTML = str;
}, delay * i);
})(array[i]);
}
}
<button onclick="myFunction()">Run</button>
<div id="textbox"></div>

You could use Symbol.iterator
let str = "Display this line of text as individual words in two second intervals";
str = str.split(' ');
let count = str.length;
let string = str[Symbol.iterator]();
let interval = setInterval(function() {
count--;
let div = document.createElement('div');
div.innerText = string.next().value;
document.body.appendChild(div);
if (count === 0) {
clearInterval(interval)
}
}, 500);

Related

totalVowels variable is not updating [duplicate]

This question already has answers here:
Count number of matches of a regex in Javascript
(8 answers)
Closed 4 years ago.
My little function doesn't seem to update the totalVowels variable.
My train of thought at the moment is: the argument is converted into an array, the array is iterated through, and if the index matches against my vowel regex, my totalVowels var will add 1 per each match.
I feel like the solution is right under my nose, but I've been changing lots of little things around to get this to work and I'm currently out of ideas.
function VowelCount(str) {
let strArr = str.split('');
let totalVowels = 0;
let vowel = /a|e|i|o|u/gi
for (let i = 0; i < strArr.length; i++) {
if (strArr[i] === vowel) { totalVowels++ }
}
return totalVowels;
}
console.log(VowelCount('vowel'));
Use .match() instead of strArr[i] === vowel for your if condition check because you're using a regex:
function VowelCount(str) {
let strArr = str.split('');
let totalVowels = 0;
let vowel = /a|e|i|o|u/gi
for (let i = 0; i < strArr.length; i++) {
if (strArr[i].match(vowel)) {
totalVowels++
}
}
return totalVowels;
}
console.log(VowelCount('hello there'));

Regex for same word shows first false and then true [duplicate]

This question already has answers here:
Why does a RegExp with global flag give wrong results?
(7 answers)
Closed 6 years ago.
I have problem with testing string with regex.
After iterations "aab", "aab", "aba".. here comes the problem when testing string "baa" first time is ok, result is false because regex test is setup to check is there repeating letter inside string, but when testing again "baa" result is now true. Why is this happening?
Here is the code:
//Here are function for swaping letters
String.prototype.swapLetters=function(index) {
var temp = this.split("");
var n = temp[index];
temp[index]=temp[index+1]; temp[index+1]=n;
var str1 = temp.join("");
return str1;
}
function permAlone(str) {
//the function for calculating number of total combinations
function returnFactorial(num){
if(num===0){
return 1;
} else {
return returnFactorial(num-1)*num;
}
}
var combs = returnFactorial(str.length);
var c = 0;
var permutations = 0;
var reg = new RegExp(/(.)\1+/g);
for (var i = 0; i < combs; i++) {
if(c>=str.length-1){
c = 0;
}
str = str.swapLetters(c);
if(!reg.test(str)){
permutations++;
console.log(str);
}
c++;
}
}
permAlone('aab');
Firstly, the condition you have if(!reg.test(str)){} should not have a !(not) in it if you intend to identify a regex match. I am not sure if that is what you wanted, though.
Secondly, I removed the 'global' match flag 'g' so that the regex is basically "reset" to start matching from the beginning of the text before each execution. This is because a single RegExp object when executed multiple times, starts to match the text each time from the last match index. This can give you a detailed explanation for this. Why RegExp with global flag in Javascript give wrong results?.
Try this.
//Here are function for swaping letters
String.prototype.swapLetters=function(index) {
var temp = this.split("");
var n = temp[index];
temp[index]=temp[index+1]; temp[index+1]=n;
var str1 = temp.join("");
return str1;
}
function permAlone(str) {
//the function for calculating number of total combinations
function returnFactorial(num){
if(num===0){
return 1;
} else {
return returnFactorial(num-1)*num;
}
}
var combs = returnFactorial(str.length);
var c = 0;
var permutations = 0;
var reg = new RegExp(/(\w)\1+/);
for (var i = 0; i < combs; i++) {
if(c>=str.length-1){
c = 0;
}
str = str.swapLetters(c);
console.log("case : " + str);
if(reg.test(str)){
permutations++;
console.log(str + " has repeated letters");
}
c++;
}
}
permAlone('aab');

Running text effect

I have some words like "beautiful","awesome","nice" and "wonderful".
i want to show the each word as typing text effect one after one for infinite time.
see i want like this :google forms
I have made the running text effect.
see the code below:
var myText= "beautiful";
var text = myText.split("");
var counter = 0;
function typeIt(text){
var SI=setInterval(function(){
var h1 = $("#myTypingText");
h1.append(text[counter]);
counter++;
if(counter==text.length){
clearInterval(SI);
}
},70);
}
i am not able to run this function for each word one after one for infinite time.
please help me to resolve this.
thanks in advance.
You can modify your existing function to accept an array of words, and process the words one by one.
EDIT: Expand this first snippet to see my original answer that has no delay in between words:
function typeIt(words) {
var letterIndex = 0;
var wordIndex= 0;
var h1 = $("#myTypingText");
var SI = setInterval(function() {
if (letterIndex === words[wordIndex].length) { // if at end of current word
wordIndex = (wordIndex + 1) % words.length; // go to next word
letterIndex = 0;
h1.empty(); // clear output div
}
h1.append(words[wordIndex][letterIndex]);
letterIndex++;
}, 70);
}
typeIt(["beautiful", "awesome", "nice", "wonderful"]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="myTypingText"></div>
Note that you don't need to .split("") the word, because you can directly reference the individual characters in a string with the same square-bracket syntax as referencing array elements, e.g., "text"[2] is "x".
You can use setTimeout() to control the delay in between words:
function typeIt(words) {
var letterIndex = 0;
var wordIndex = 0;
var h1 = $("#myTypingText");
(function nextWord() {
var SI = setInterval(function() {
h1.append(words[wordIndex][letterIndex]);
letterIndex++;
if (letterIndex === words[wordIndex].length) {
wordIndex = (wordIndex + 1) % words.length;
letterIndex = 0;
clearInterval(SI);
setTimeout(function() {
h1.empty();
nextWord();
}, 1000); // delay between words
}
}, 70); // delay between letters
})();
}
typeIt(["beautiful", "awesome", "nice", "wonderful"]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="myTypingText"></div>
Note that this isn't necessarily the cleanest way to structure the code, but it seemed the quickest way to modify what you already had to achieve the desired output.
Further reading: Immediately Invoked Function Expressions (IIFEs).

word frequency in javascript

How can I implement javascript function to calculate frequency of each word in a given sentence.
this is my code:
function search () {
var data = document.getElementById('txt').value;
var temp = data;
var words = new Array();
words = temp.split(" ");
var uniqueWords = new Array();
var count = new Array();
for (var i = 0; i < words.length; i++) {
//var count=0;
var f = 0;
for (j = 0; j < uniqueWords.length; j++) {
if (words[i] == uniqueWords[j]) {
count[j] = count[j] + 1;
//uniqueWords[j]=words[i];
f = 1;
}
}
if (f == 0) {
count[i] = 1;
uniqueWords[i] = words[i];
}
console.log("count of " + uniqueWords[i] + " - " + count[i]);
}
}
am unable to trace out the problem ..any help is greatly appriciated.
output in this format:
count of is - 1
count of the - 2..
input: this is anil is kum the anil
Here is a JavaScript function to get the frequency of each word in a sentence:
function wordFreq(string) {
var words = string.replace(/[.]/g, '').split(/\s/);
var freqMap = {};
words.forEach(function(w) {
if (!freqMap[w]) {
freqMap[w] = 0;
}
freqMap[w] += 1;
});
return freqMap;
}
It will return a hash of word to word count. So for example, if we run it like so:
console.log(wordFreq("I am the big the big bull."));
> Object {I: 1, am: 1, the: 2, big: 2, bull: 1}
You can iterate over the words with Object.keys(result).sort().forEach(result) {...}. So we could hook that up like so:
var freq = wordFreq("I am the big the big bull.");
Object.keys(freq).sort().forEach(function(word) {
console.log("count of " + word + " is " + freq[word]);
});
Which would output:
count of I is 1
count of am is 1
count of big is 2
count of bull is 1
count of the is 2
JSFiddle: http://jsfiddle.net/ah6wsbs6/
And here is wordFreq function in ES6:
function wordFreq(string) {
return string.replace(/[.]/g, '')
.split(/\s/)
.reduce((map, word) =>
Object.assign(map, {
[word]: (map[word])
? map[word] + 1
: 1,
}),
{}
);
}
JSFiddle: http://jsfiddle.net/r1Lo79us/
I feel you have over-complicated things by having multiple arrays, strings, and engaging in frequent (and hard to follow) context-switching between loops, and nested loops.
Below is the approach I would encourage you to consider taking. I've inlined comments to explain each step along the way. If any of this is unclear, please let me know in the comments and I'll revisit to improve clarity.
(function () {
/* Below is a regular expression that finds alphanumeric characters
Next is a string that could easily be replaced with a reference to a form control
Lastly, we have an array that will hold any words matching our pattern */
var pattern = /\w+/g,
string = "I I am am am yes yes.",
matchedWords = string.match( pattern );
/* The Array.prototype.reduce method assists us in producing a single value from an
array. In this case, we're going to use it to output an object with results. */
var counts = matchedWords.reduce(function ( stats, word ) {
/* `stats` is the object that we'll be building up over time.
`word` is each individual entry in the `matchedWords` array */
if ( stats.hasOwnProperty( word ) ) {
/* `stats` already has an entry for the current `word`.
As a result, let's increment the count for that `word`. */
stats[ word ] = stats[ word ] + 1;
} else {
/* `stats` does not yet have an entry for the current `word`.
As a result, let's add a new entry, and set count to 1. */
stats[ word ] = 1;
}
/* Because we are building up `stats` over numerous iterations,
we need to return it for the next pass to modify it. */
return stats;
}, {} );
/* Now that `counts` has our object, we can log it. */
console.log( counts );
}());
const sentence = 'Hi my friend how are you my friend';
const countWords = (sentence) => {
const convertToObject = sentence.split(" ").map( (i, k) => {
return {
element: {
word: i,
nr: sentence.split(" ").filter(j => j === i).length + ' occurrence',
}
}
});
return Array.from(new Set(convertToObject.map(JSON.stringify))).map(JSON.parse)
};
console.log(countWords(sentence));
Here is an updated version of your own code...
<!DOCTYPE html>
<html>
<head>
<title>string frequency</title>
<style type="text/css">
#text{
width:250px;
}
</style>
</head>
<body >
<textarea id="txt" cols="25" rows="3" placeholder="add your text here"> </textarea></br>
<button type="button" onclick="search()">search</button>
<script >
function search()
{
var data=document.getElementById('txt').value;
var temp=data;
var words=new Array();
words=temp.split(" ");
var unique = {};
for (var i = 0; i < words.length; i++) {
var word = words[i];
console.log(word);
if (word in unique)
{
console.log("word found");
var count = unique[word];
count ++;
unique[word]=count;
}
else
{
console.log("word NOT found");
unique[word]=1;
}
}
console.log(unique);
}
</script>
</body>
I think your loop was overly complicated. Also, trying to produce the final count while still doing your first pass over the array of words is bound to fail because you can't test for uniqueness until you have checked each word in the array.
Instead of all your counters, I've used a Javascript object to work as an associative array, so we can store each unique word, and the count of how many times it occurs.
Then, once we exit the loop, we can see the final result.
Also, this solution uses no regex ;)
I'll also add that it's very hard to count words just based on spaces. In this code, "one, two, one" will results in "one," and "one" as being different, unique words.
While both of the answers here are correct maybe are better but none of them address OP's question (what is wrong with the his code).
The problem with OP's code is here:
if(f==0){
count[i]=1;
uniqueWords[i]=words[i];
}
On every new word (unique word) the code adds it to uniqueWords at index at which the word was in words. Hence there are gaps in uniqueWords array. This is the reason for some undefined values.
Try printing uniqueWords. It should give something like:
["this", "is", "anil", 4: "kum", 5: "the"]
Note there no element for index 3.
Also the printing of final count should be after processing all the words in the words array.
Here's corrected version:
function search()
{
var data=document.getElementById('txt').value;
var temp=data;
var words=new Array();
words=temp.split(" ");
var uniqueWords=new Array();
var count=new Array();
for (var i = 0; i < words.length; i++) {
//var count=0;
var f=0;
for(j=0;j<uniqueWords.length;j++){
if(words[i]==uniqueWords[j]){
count[j]=count[j]+1;
//uniqueWords[j]=words[i];
f=1;
}
}
if(f==0){
count[i]=1;
uniqueWords[i]=words[i];
}
}
for ( i = 0; i < uniqueWords.length; i++) {
if (typeof uniqueWords[i] !== 'undefined')
console.log("count of "+uniqueWords[i]+" - "+count[i]);
}
}
I have just moved the printing of count out of the processing loop into a new loop and added a if not undefined check.
Fiddle: https://jsfiddle.net/cdLgaq3a/
I had a similar assignment. This is what I did:
Assignment : Clean the following text and find the most frequent word (hint, use replace and regular expressions).
const sentence = '%I $am#% a %tea#cher%, &and& I lo%#ve %te#a#ching%;. The#re $is no#th#ing; &as& mo#re rewarding as educa#ting &and& #emp%o#weri#ng peo#ple. ;I found tea#ching m%o#re interesting tha#n any ot#her %jo#bs. %Do#es thi%s mo#tiv#ate yo#u to be a tea#cher!? %Th#is 30#Days&OfJavaScript &is al#so $the $resu#lt of &love& of tea&ching'
console.log(`\n\n 03.Clean the following text and find the most frequent word (hint, use replace and regular expressions) \n\n ${sentence} \n\n`)
console.log(`Cleared sentence : ${sentence.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()#]/g, "")}`)
console.log(mostFrequentWord(sentence))
function mostFrequentWord(sentence) {
sentence = sentence.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()#]/g, "").trim().toLowerCase()
let sentenceArray = sentence.split(" ")
let word = null
let count = 0
for (i = 0; i < sentenceArray.length; i++) {
word = sentenceArray[i]
count = sentence.match(RegExp(sentenceArray[i], 'gi')).length
if (count > count) {
count = count
word = word
}
}
return `\n Count of most frequent word "${word}" is ${count}`
}
I'd go with Sampson's match-reduce method for slightly better efficiency. Here's a modified version of it that is more production-ready. It's not perfect, but it should cover the vast majority of scenarios (i.e., "good enough").
function calcWordFreq(s) {
// Normalize
s = s.toLowerCase();
// Strip quotes and brackets
s = s.replace(/["“”(\[{}\])]|\B['‘]([^'’]+)['’]/g, '$1');
// Strip dashes and ellipses
s = s.replace(/[‒–—―…]|--|\.\.\./g, ' ');
// Strip punctuation marks
s = s.replace(/[!?;:.,]\B/g, '');
return s.match(/\S+/g).reduce(function(oFreq, sWord) {
if (oFreq.hasOwnProperty(sWord)) ++oFreq[sWord];
else oFreq[sWord] = 1;
return oFreq;
}, {});
}
calcWordFreq('A ‘bad’, “BAD” wolf-man...a good ol\' spook -- I\'m frightened!') returns
{
"a": 2
"bad": 2
"frightened": 1
"good": 1
"i'm": 1
"ol'": 1
"spook": 1
"wolf-man": 1
}

Text Analysis webpage does not work

For a homework assignment, I have to make a web page that has a text area. The user inputs some text, hits the analyse button, and the output is supposed to be a list of the frequency of words of a given number of characters. For example, "one two three" would be two words of three characters and one word of five characters. The text area works fine, but I can"t seem to get the output to appear.
Here is the html:
<body>
<textarea id="text" rows="26" cols="80"></textarea>
<form>
<input type="button" id="analyse" value="Analyse Text">
</form>
<div id="output"></div>
</body>
The JavaScript file has 6 functions. The first function returns an array that stores the number of characters for each word in the input textarea:
function getWordInfo() {
var text = document.getElementById("text").value;
//the variable wordArray uses a regular expression to parse the input
var wordArray = text.split("/\w\w+/g");
var arrayLength = wordArray.length;
var charArray = [];
for (var i = 0; i < arrayLength; i++) {
var splitWord = wordArray[i].split("");
var wordLength = splitWord.length;
charArray.push(wordLength);
}
return charArray;
}
The second function is a simple object constructor that has a name property and a count property. The count property has a default value of 0.
function obCon(name,count) { //object constructor
this.name = name;
this.count = count;
count = typeof count !== "undefined" ? count : 0;
}
The third function returns an array that stores word objects. Each object has a name property and a count property. The name property is the number of characters in a word. The count property counts how many times an object with a given name property appears.
function arCon() { //array constructor
var charNum = getWordInfo();
var arrayLength = charNum.length;
var obArr = [];
for (var i = 0; i < arrayLength; i++) {
if (typeof obArr.indexOf( newOb.name === charNum[i] ) != "undefined" ) { // checks if the object needed exists
obArr.indexOf( newOb.name === charNum[i] ).count = obArr.indexOf( newOb.name === charNum[i] ).count + 1;
}else{
var newOb = new obCon(charNum[i]);
newOb.count = newOb.count + 1;
obArr.push(newOb);
}
}
return obArr;
}
The fourth function is a string formatter, meant format the objects from arCon into a single readable string, then store it in an array.
function formatter() {
var strAr = arCon();
var arrayLength = strAr.length;
var formatStr = [];
for (var i = 0; i < arrayLength; i++) {
var str = "Number of characters: " + strAr[i].name + ", Number of words with this length: " + strAr[i].count;
formatStr.push(str);
}
return formatStr;
}
The fifth function is called for an event handler, meant to handle the click of the analyse button. On click, it is meant to get the div tag, get the formatted array, then loop though each element in the array, where it creates a p element element, pulls the formatted string, sets the p element value to the formatted string, then appends the p element to the div tag.
function analyseButtonClick() {
var div = document.getElementById("output");
var str = formatter();
var arrayLength = str.length;
for (var i = 0; i < arrayLength; i++) {
var par = document.createElement("p");
var format = str[i];
par.value = format;
div.appendChild(par);
}
}
The sixth function is an init function that handles the button click.
function init() {
var button = document.getElementById("analyse");
button.onclick = analyseButtonClick;
}
window.onload = init;
I have run this though a validator, and it shows me that there are no syntax errors, so it must be a logic error. However, all the functions seem to do what they are supposed to do, so I am not sure where I went wrong.
edit1: ok, have replaced the third function with four new functions. a function that returns the highest number in the array returned by getWordInfo, a function that constructs objects with name properties from 2 up to that number, a function that update the count properties of the objects, and a function that removes unused objects. here they are:
function maxNum() {
var array = getWordInfo();
var num = Math.max.apply(Math, array);
return num;
}
function objArrCon() { //object array constructor
var num = maxNum();
var array1 = [];
for (var i = 2; i === num; i++) {
var myObj = new objCon(i,0);
array1.push(myObj);
}
return array1;
}
function objArrParse() { //updates the object with word info
var array1 = getWordInfo();
var array2 = objArrCon();
var loopLength1 = array1.length;
var loopLength2 = array2.length;
for (var i = 0; i < loopLength1; i++) {
for (var m = 0; m < loopLength2; m++) {
if (array2[m].name === array1[i]) {
array2[m].count++;
}
}
}
return array2;
}
function objArrTrun() { //removes unused objects
var array1 = objArrParse();
var loopLength = array1.length;
for (var i = 0;i < loopLength; i++) {
if (array1[i].count === 0) {
array.splice(i, array1);
}
}
return array1;
}
still does not work, but im getting there!
I have written this in jQuery because 1) it's not my homework assignment and 2) it would be good practice (for you) to translate it back into normal JavaScript. You'll need to rewrite the event handler, along with the two getters/setters ($("text area").val() and $("tbody").append()).
Your main problem is that you have made your solution too complicated! If all you want to do is display the number, Y, of words with length, X, then you should do the following:
Grab the value of the text area and store in text
Remove all non-alphanumeric characters.
Split the text string wherever one or more spaces is present and store it in text
Loop through the array and map each array element, based on its length, to wordMap
Loop through wordMap and append it to wherever in the DOM you want the results to be
fiddle
JavaScript
$("#analyze").click(function () {
var text = $("textarea").val().replace(/[^\d\w ]+/g," ").split(/[ ]+/);
// this grabs the value of the text area, replaces all non-numerical
// and non-alphabetical characters with "", and then splits it into an array
// wherever one or more spaces is present.
var wordMap = {};
// makes an object that will be used as an associative array
text.forEach(function (d) {
// loops through the array
// if there is no key called d.length in wordMap
if (!wordMap[d.length])
wordMap[d.length] = 1; // set its count to one
else
wordMap[d.length]++; //otherwise, increment it
})
// loop through all the properties of wordMap
for (var x in wordMap) {
$("tbody").append("<tr><td>" + x + "</td><td>"
+ wordMap[x] + "</td></tr");
}
console.log(wordMap);
})
HTML
<textarea placeholder="type something..."></textarea>
<button id="analyze">Click to analyze</button>
<div id="results">
<table>
<thead>
<th>Length of Word</th>
<th>Number of Occurrences</th>
</thead>
<tbody>
</tbody>
</table>
</div>

Categories

Resources