DRY programming with JavaScript Mad Libs Challenge - javascript

I am new to programming.
I know that I could use functions and loops to keep from repeating this code, but I need help. Anyone?
var questions = 3;
var questionsCount = ' [' + questions + ' questions left]';
var adjective = prompt('Please type an adjective' + questionsCount);
questions -= 1;
questionsCount = ' [' + questions + ' questions left]';
var verb = prompt('Please type a verb' + questionsCount);
questions -= 1;
questionsCount = ' [' + questions + ' questions left]';
var noun = prompt('Please type a noun' + questionsCount);
alert('All done. Ready for the message?');
var sentence = "There once was a " + adjective;
sentence += ' programmer who wanted to use JavaScript to ' + verb;
sentence += ' the ' + noun + '.';
document.write(sentence);

I'd use a string template which contains, eg, {{noun}} to be replaced with a noun, which uses a regular expression to prompt the user for replacements to make:
const template = 'There once was a {{adjective}} programmer who wanted to use JavaScript to {{verb}} the {{noun}}.';
let questions = 3;
const result = template.replace(
/{{(.*?)}}/g,
(_, typeOfSpeechNeeded) => prompt(`Please type a ${typeOfSpeechNeeded}, ${questions--} question(s) left`)
);
console.log(result);
The regular expression
{{(.*?)}}
matches {{, followed by some characters, followed by }}, where those characters are put into a capturing group - this allows the .replace to examine the capturing group to determine what typeOfSpeechNeeded to display in the prompt.
The /g in the regular expression makes it global, which replaces all matches, not just the first match.
The backtick string is just a more readable way of interpolating strings:
prompt(`Please type a ${typeOfSpeechNeeded}, ${questions--} question(s) left`)
is equivalent to
prompt('Please type a ' + typeOfSpeechNeeded + ', ' + questions-- + ' question(s) left')

Related

Adding the first 3 words in a string to the last 3 words in a string and skip what's in the middle in JS [duplicate]

This question already has answers here:
How to get first 2 words?
(3 answers)
Closed 2 months ago.
How can I get the first 3 words from a string and add them to the last 3 words of the same string and skip the text in the middle.
For ex.,
The string "I have something important to tell you guys"
The result should be the string "I have something tell you guys" (first 3 words and last 3 words)
My code:
let myString = "I have something important to tell you guys";
let myArray = "I have something important to tell you guys";
myArray = myArray.split(' ')
let firstThreeWords = myArray[0] + " " + myArray[1] + " " + myArray[2];
let lastThreeWords = ... //not sure what to put here
console.log(firstThreeWords + lastThreeWords)
I got the first 3 words but how can I get the last 3 words if we don't know the exact number of words in a string?
let myString = 'I have something important to tell you guys';
let myArray = 'I have something important to tell you guys';
myArray = myArray.split(' ');
let firstThreeWords = myArray[0] + ' ' + myArray[1] + ' ' + myArray[2] + ' ';
let lastThreeWords =
myArray[myArray.length - 3] +
' ' +
myArray[myArray.length - 2] +
' ' +
myArray[myArray.length - 1];
console.log(firstThreeWords + lastThreeWords);
get you're last three words from the string by decreasing the length of the array.
You can try something like that
let myString = "I have something important to tell you guys";
let myArray = myString.split(' ');
const firstThree = myArray.slice(0, 3).join(' ');
const lastThree = myArray.slice(-3).join(' ');
const result = firstThree + " " + lastThree;
console.log(result);

javascript regex match between two strings and count the number of characters inside

I'm very new to js and programming, and i'd appreciate some help.
Suppose I have the following array (It is a single element array, they are not seperate elements)
var array =
[
'Foo\n' +
'bar23123\n' +
'barbarfoo\n' +
'foo, bar foo\n' +
'foo\n' +
'\n' +
'\n' +
'Bar\n' +
'\n' +
'\n'
]
Assuming there are multiple elements in the array, how can I write a regex expression to count and match the number of literal '\n' in between Foo\n and Bar\n in each element.
I wrote a partial solution to iterate through and return the count of all \n but how do I narrow my search to only return the values in between the two strings?
Here is the code:
let slashN = [];
for(let slash of array){
var reg = RegExp('\\n' , 'g')
slashN.push(slash.match(reg).length)
}
console.log(slashN);
I get the correct count, but I want to refine the regex expression.
Any help is highly appreciated.
You may use
var reg = /(?<=Foo\n.*?)\n(?=.*?Bar\n)/gs;
See the regex demo. Details:
(?<=Foo\n.*?) - a positive lookbehind that matches a location that is immediately preceded with Foo + newline and then any 0 or more chars, as few as possible
\n - a newline
(?=.*?Bar\n) - a positive lookahead that matches a location immediately followed with any 0+ chars as few as possible and then Bar + newline.
JavaScript demo:
var array =
[
'Foo\n' +
'bar23123\n' +
'barbarfoo\n' +
'foo, bar foo\n' +
'foo\n' +
'\n' +
'\n' +
'Bar\n' +
'\n' +
'\n'
];
let slashN = [];
for(let slash of array){
var reg = /(?<=Foo\n.*?)\n(?=.*?Bar\n)/gs;
slashN.push(slash.match(reg).length)
}
console.log(slashN);

How to find hyphenated Strings

I search for specific words in a text and find them too. However, if the word I am looking for is divided into two lines by a hyphenation, the word will not be found. Here is a sample code.
searchString = "Hollywood";
newString = "";
text = "In India Hollywood is called Bollywood.";
var i = 0;
i = text.indexOf(searchString, i);
newString += text.substring(0, i) + " <<here begins my searchString>> " + text.substr(i, searchString.length) + " <<here ends my searchString>> " +
text.substring(i + searchString.length);
console.log(newString);
If the searchString Hollywood looks like
Holly-<br>wood
it will not be found.
How can I solve this problem in my Javascript code?
There are a few ways you could do it, but one of them would be to get rid of the - altogether if they're present:
searchString = "Hollywood";
newString = "";
text = "In India Holly-<br>wood is called Bollywood.";
filteredText = text.replace(/-<br>/,'');
var i = 0;
i = filteredText.indexOf(searchString, i);
newString += filteredText.substring(0, i) + " <<here begins my searchString>> " + filteredText.substr(i, searchString.length) + " <<here ends my searchString>> " +
filteredText.substring(i + searchString.length);
console.log(newString);
In this case, we just replace the -<br> characters with an empty string. It might not be the best approach, but refining it would depend on the context in which you intend to use it.
I hope that the regex and replace idea can help you customize a solution that best fit your needs.

Passing a variable through to an array mid string

I have an array like this:
var array = [
'Hey there, ' + name + '!',
'It\'s nice to see ' + name + ' join us.',
'Everybody welcome ' + name + '!',
'Thanks,' + name
]
I get an error stating that name is undefined, so if I put name = ''; before it, and loop through the array, it just says
Hey there, !
It's nice to see join us.
Everybody welcome !
Thanks,
Is there a way I can do something like:
name = 'Albz'
console.log(array[0]);
and have it echo out
Hey there, Albz!
The name variable is dynamic and changes on each iteration of forEach, so it can't be hardcoded, and I'd like to not have to redeclare the array every single time as it's quite lengthy.
Is there a way to do this?
var nameArr = ['Ayan', 'Arnab', 'Akash'];
function process(name) {
return [
'Hey there, ' + name + '!',
'It\'s nice to see ' + name + ' join us.',
'Everybody welcome ' + name + '!',
'Thanks,' + name
];
}
for (var i = 0, len = nameArr.length; i < len; i += 1) {
console.log(process(nameArr[i])[0]);
}

How to identify the following code patterns

I have a pattern of js promises that I want to identify for several keywords
For example if I put code like:
var deferred = Q.defer();
And in the file I have also the following respective value
deferred.reject(err);
deferred.resolve();
return deferred.promise;
The complete code
EXAMPLE 1
function writeError(errMessage) {
var deferred = Q.defer();
fs.writeFile("errors.log", errMessage, function (err) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve();
}
});
return deferred.promise;
}
And I want that if I put large code file (as string) to find that
this file contain the pattern
Another example
var d = Q.defer(); /* or $q.defer */
And in the file you have also the following respective value
d.resolve(val);
d.reject(err);
return d.promise;
Complete EXAMPLE 2
function getStuffDone(param) {
var d = Q.defer(); /* or $q.defer */
Promise(function(resolve, reject) {
// or = new $.Deferred() etc.
myPromiseFn(param+1)
.then(function(val) { /* or .done */
d.resolve(val);
}).catch(function(err) { /* .fail */
d.reject(err);
});
return d.promise; /* or promise() */
}
There is open sources which can be used to do such analysis(provide a pattern and it will found...)
There is some more complex patters with childProcess but for now this is OK
:)
The following regular expression may look a bit scary but has been built from simple concepts and allows a little more leeway than you mentioned - e.g. extra whitespace, different variable names, omission of var etc. Seems to work for both examples - please see if it meets your needs.
([^\s\r\n]+)\s*=\s*(?:Q|\$q)\.defer\s*\(\s*\)\s*;(?:\r?\n|.)*(?:\s|\r?\n)(?:\1\.reject\(\w+\)\s*;(?:\r?\n|.)*(?:\s|\r?\n)\1\.resolve\(\s*\w*\)\s*;|\1\.resolve\(\s*\w*\)\s*;(?:\r?\n|.)*(?:\s|\r?\n)\1\.reject\(\w+\)\s*;)(?:\r?\n|.)*(?:\s|\r?\n)return\s+(?:\1\.)?promise\s*;
Debuggex Demo
UPDATE: I made one correction to the code, i.e. changed set[2] to set[set.length - 1] to accommodate query sets of any size. I then applied the exact same algorithm to your two examples.
The solution I provide follows some rules that I think are reasonable for the type of search you are proposing. Assume you are looking for four lines, ABCD (case insensitive, so it will find ABCD or abcd or aBcD):
Multiple match sets can be found in a single file, i.e. it will find two sets in ABCDabcd.
Regex's are used for individual lines, meaning that variations can be included. (As only one consequence of this, it won't matter if you have a comment at the end of a matching line in your code.)
The patterns sought must always be on different lines, e.g. A and B can't be on the same line.
The matched set must be complete, e.g. it will not find ABC or ABD.
The matched set must be uninterrupted, i.e. it will not find anything in ABCaD. (Importantly, this also means that is will not find anything in overlapping sets, e.g. ABCaDbcd. You could argue that this is too limiting. However, in this example, which should be found, ABCD or abcd? The answer is arbitrary, and arbitrariness is difficult to code. Moreover, based on the examples you showed, such overlapping would not typically be expected, so this edge case seems unlikely, making this limitation reasonable.)
The matched set must be internally non-repeating, e.g. it will not find ABbCD. However, with AaBCD, it will find a set, i.e. it will find aBCD.
Embedded sets are allowed, but only the internal one will be found, e.g. with ABabcdCD, only abcd will be found.
The code snippet below shows an example search. It does not demonstrate all of the edge cases. However, it does show the overall functionality.
var queryRegexStrs = [
"I( really)? (like|adore) strawberry",
"I( really)? (like|adore) chocolate",
"I( really)? (like|adore) vanilla"
];
var codeStr =
"....\n" +
"Most people would say 'I like vanilla'\n" +
"....\n" +
"....\n" +
"....\n" +
"....\n" +
"Amir's taste profile:\n" +
"....\n" +
"I like strawberry\n" +
"....\n" +
"....\n" +
"I told Billy that I really adore chocolate a lot\n" +
"....\n" +
"I like vanilla most of the time\n" +
"....\n" +
"Let me emphasize that I like strawberry\n" +
"....\n" +
"....\n" +
"....\n" +
"....\n" +
"Juanita's taste profile:\n" +
"....\n" +
"I really adore strawberry\n" +
"I like vanilla\n" +
"....\n" +
"....\n" +
"....\n" +
"....\n" +
"Rachel's taste profile:\n" +
"I adore strawberry\n" +
"....\n" +
"Sometimes I like chocolate, I guess\n" +
"....\n" +
"I adore vanilla\n" +
"....\n" +
"....\n" +
"....\n" +
"....\n" +
"";
// allow for different types of end-of-line characters or character sequences
var endOfLineStr = "\n";
var matchSets = search(queryRegexStrs, codeStr, endOfLineStr);
function search(queryRegexStrs, codeStr, endOfLineStr) {
// break the large code string into an array of line strings
var codeLines = codeStr.split(endOfLineStr);
// remember the number of lines being sought
var numQueryLines = queryRegexStrs.length;
// convert the input regex strings into actual regex's in a parallel array
var queryRegexs = queryRegexStrs.map(function(queryRegexStr) {
return new RegExp(queryRegexStr);
});
// search the array for each query line
// to find complete, uninterrupted, non-repeating sets of matches
// make an array to hold potentially multiple match sets from the same file
var matchSets = [];
// prepare to try finding the next match set
var currMatchSet;
// keep track of which query line number is currently being sought
var idxOfCurrQuery = 0;
// whenever looking for a match set is (re-)initialized,
// start looking again for the first query,
// and forget any previous individual query matches that have been found
var resetCurrQuery = function() {
idxOfCurrQuery = 0;
currMatchSet = [];
};
// check each line of code...
codeLines.forEach(function(codeLine, codeLineNum, codeLines) {
// ...against each query line
queryRegexs.forEach(function(regex, regexNum, regexs) {
// check if this line of code is a match with this query line
var matchFound = regex.test(codeLine);
// if so, remember which query line it matched
if (matchFound) {
// if this code line matches the first query line,
// then reset the current query and continue
if (regexNum === 0) {
resetCurrQuery();
}
// if this most recent individual match is the one expected next, proceed
if (regexNum === idxOfCurrQuery) {
// temporarily remember the line number of this most recent individual match
currMatchSet.push(codeLineNum);
// prepare to find the next query in the sequence
idxOfCurrQuery += 1;
// if a whole query set has just been found, then permanently remember
// the corresponding code line numbers, and reset the search
if (idxOfCurrQuery === numQueryLines) {
matchSets.push(currMatchSet);
resetCurrQuery();
}
// if this most recent match is NOT the one expected next in the sequence,
// then start over in terms of starting to look again for the first query
} else {
resetCurrQuery();
}
}
});
});
return matchSets;
}
// report the results
document.write("<b>The code lines being sought:</b>");
document.write("<pre>" + JSON.stringify(queryRegexStrs, null, 2) + "</pre>");
document.write("<b>The code being searched:</b>");
document.write(
"<pre><ol start='0'><li>" +
codeStr.replace(new RegExp("\n", "g"), "</li><li>") +
"</li></ol></pre>"
);
document.write("<b>The code line numbers of query 'hits', grouped by query set:</b>");
document.write("<pre>" + JSON.stringify(matchSets) + "</pre>");
document.write("<b>One possible formatted output:</b>");
var str = "<p>(Note that line numbers are 0-based...easily changed to 1-based if desired)</p>";
str += "<pre>";
matchSets.forEach(function(set, setNum, arr) {
str += "Matching code block #" + (setNum + 1) + ": lines " + set[0] + "-" + set[set.length - 1] + "<br />";
});
str += "</pre>";
document.write(str);
Here is the exact same algorithm, just using your original examples 1 and 2. Note a couple of things. First of all, anything that needs escaping in the regex strings actually needs double-escaping, e.g. in order to find a literal opening parenthesis you need to include "\\(" not just "\(". Also, the regex's perhaps seem a little complex. I have two comments about this. First: a lot of that is just finding the literal periods and parentheses. However, second, and importantly: the ability to use complex regex's is part of the power (read "flexibility") of this entire approach. e.g. The examples you provided required some alternation where, e.g., "a|b" means "find a OR b".
var queryRegexStrs = [
"var deferred = Q\\.defer\\(\\);",
"deferred\\.reject\\(err\\);",
"deferred\\.resolve\\(\\);",
"return deferred\\.promise;"
];
var codeStr =
'function writeError(errMessage) {' + "\n" +
' var deferred = Q.defer();' + "\n" +
' fs.writeFile("errors.log", errMessage, function (err) {' + "\n" +
' if (err) {' + "\n" +
' deferred.reject(err);' + "\n" +
' } else {' + "\n" +
' deferred.resolve();' + "\n" +
' }' + "\n" +
' });' + "\n" +
' return deferred.promise;' + "\n" +
'}' + "\n" +
'';
// allow for different types of end-of-line characters or character sequences
var endOfLineStr = "\n";
var matchSets = search(queryRegexStrs, codeStr, endOfLineStr);
function search(queryRegexStrs, codeStr, endOfLineStr) {
// break the large code string into an array of line strings
var codeLines = codeStr.split(endOfLineStr);
// remember the number of lines being sought
var numQueryLines = queryRegexStrs.length;
// convert the input regex strings into actual regex's in a parallel array
var queryRegexs = queryRegexStrs.map(function(queryRegexStr) {
return new RegExp(queryRegexStr);
});
// search the array for each query line
// to find complete, uninterrupted, non-repeating sets of matches
// make an array to hold potentially multiple match sets from the same file
var matchSets = [];
// prepare to try finding the next match set
var currMatchSet;
// keep track of which query line number is currently being sought
var idxOfCurrQuery = 0;
// whenever looking for a match set is (re-)initialized,
// start looking again for the first query,
// and forget any previous individual query matches that have been found
var resetCurrQuery = function() {
idxOfCurrQuery = 0;
currMatchSet = [];
};
// check each line of code...
codeLines.forEach(function(codeLine, codeLineNum, codeLines) {
// ...against each query line
queryRegexs.forEach(function(regex, regexNum, regexs) {
// check if this line of code is a match with this query line
var matchFound = regex.test(codeLine);
// if so, remember which query line it matched
if (matchFound) {
// if this code line matches the first query line,
// then reset the current query and continue
if (regexNum === 0) {
resetCurrQuery();
}
// if this most recent individual match is the one expected next, proceed
if (regexNum === idxOfCurrQuery) {
// temporarily remember the line number of this most recent individual match
currMatchSet.push(codeLineNum);
// prepare to find the next query in the sequence
idxOfCurrQuery += 1;
// if a whole query set has just been found, then permanently remember
// the corresponding code line numbers, and reset the search
if (idxOfCurrQuery === numQueryLines) {
matchSets.push(currMatchSet);
resetCurrQuery();
}
// if this most recent match is NOT the one expected next in the sequence,
// then start over in terms of starting to look again for the first query
} else {
resetCurrQuery();
}
}
});
});
return matchSets;
}
// report the results
document.write("<b>The code lines being sought:</b>");
document.write("<pre>" + JSON.stringify(queryRegexStrs, null, 2) + "</pre>");
document.write("<b>The code being searched:</b>");
document.write(
"<pre><ol start='0'><li>" +
codeStr.replace(new RegExp("\n", "g"), "</li><li>") +
"</li></ol></pre>"
);
document.write("<b>The code line numbers of query 'hits', grouped by query set:</b>");
document.write("<pre>" + JSON.stringify(matchSets) + "</pre>");
document.write("<b>One possible formatted output:</b>");
var str = "<p>(Note that line numbers are 0-based...easily changed to 1-based if desired)</p>";
str += "<pre>";
matchSets.forEach(function(set, setNum, arr) {
str += "Matching code block #" + (setNum + 1) + ": lines " + set[0] + "-" + set[set.length - 1] + "<br />";
});
str += "</pre>";
document.write(str);
Here is the exact same algorithm, just using your original example 2:
var queryRegexStrs = [
"var d = (Q\\.defer\\(\\)|\\$q\\.defer);",
"d\\.resolve\\(val\\);",
"d\\.reject\\(err\\);",
"return d\\.promise(\\(\\))?;"
];
var codeStr =
"...." + "\n" +
"...." + "\n" +
"...." + "\n" +
"function getStuffDone(param) {" + "\n" +
" var d = Q.defer();" + "\n" +
"" + "\n" +
" Promise(function(resolve, reject) {" + "\n" +
" // or = new $.Deferred() etc." + "\n" +
" myPromiseFn(param+1)" + "\n" +
" .then(function(val) { /* or .done */" + "\n" +
" d.resolve(val);" + "\n" +
" }).catch(function(err) { /* .fail */" + "\n" +
" d.reject(err);" + "\n" +
" });" + "\n" +
" return d.promise;" + "\n" +
"" + "\n" +
"}" + "\n" +
"...." + "\n" +
"...." + "\n" +
"...." + "\n" +
"function getStuffDone(param) {" + "\n" +
" var d = $q.defer;" + "\n" +
"" + "\n" +
" Promise(function(resolve, reject) {" + "\n" +
" // or = new $.Deferred() etc." + "\n" +
" myPromiseFn(param+1)" + "\n" +
" .then(function(val) { /* or .done */" + "\n" +
" d.resolve(val);" + "\n" +
" }).catch(function(err) { /* .fail */" + "\n" +
" d.reject(err);" + "\n" +
" });" + "\n" +
" return d.promise();" + "\n" +
"" + "\n" +
"}" + "\n" +
"...." + "\n" +
"...." + "\n" +
"...." + "\n" +
"";
// allow for different types of end-of-line characters or character sequences
var endOfLineStr = "\n";
var matchSets = search(queryRegexStrs, codeStr, endOfLineStr);
function search(queryRegexStrs, codeStr, endOfLineStr) {
// break the large code string into an array of line strings
var codeLines = codeStr.split(endOfLineStr);
// remember the number of lines being sought
var numQueryLines = queryRegexStrs.length;
// convert the input regex strings into actual regex's in a parallel array
var queryRegexs = queryRegexStrs.map(function(queryRegexStr) {
return new RegExp(queryRegexStr);
});
// search the array for each query line
// to find complete, uninterrupted, non-repeating sets of matches
// make an array to hold potentially multiple match sets from the same file
var matchSets = [];
// prepare to try finding the next match set
var currMatchSet;
// keep track of which query line number is currently being sought
var idxOfCurrQuery = 0;
// whenever looking for a match set is (re-)initialized,
// start looking again for the first query,
// and forget any previous individual query matches that have been found
var resetCurrQuery = function() {
idxOfCurrQuery = 0;
currMatchSet = [];
};
// check each line of code...
codeLines.forEach(function(codeLine, codeLineNum, codeLines) {
// ...against each query line
queryRegexs.forEach(function(regex, regexNum, regexs) {
// check if this line of code is a match with this query line
var matchFound = regex.test(codeLine);
// if so, remember which query line it matched
if (matchFound) {
// if this code line matches the first query line,
// then reset the current query and continue
if (regexNum === 0) {
resetCurrQuery();
}
// if this most recent individual match is the one expected next, proceed
if (regexNum === idxOfCurrQuery) {
// temporarily remember the line number of this most recent individual match
currMatchSet.push(codeLineNum);
// prepare to find the next query in the sequence
idxOfCurrQuery += 1;
// if a whole query set has just been found, then permanently remember
// the corresponding code line numbers, and reset the search
if (idxOfCurrQuery === numQueryLines) {
matchSets.push(currMatchSet);
resetCurrQuery();
}
// if this most recent match is NOT the one expected next in the sequence,
// then start over in terms of starting to look again for the first query
} else {
resetCurrQuery();
}
}
});
});
return matchSets;
}
// report the results
document.write("<b>The code lines being sought:</b>");
document.write("<pre>" + JSON.stringify(queryRegexStrs, null, 2) + "</pre>");
document.write("<b>The code being searched:</b>");
document.write(
"<pre><ol start='0'><li>" +
codeStr.replace(new RegExp("\n", "g"), "</li><li>") +
"</li></ol></pre>"
);
document.write("<b>The code line numbers of query 'hits', grouped by query set:</b>");
document.write("<pre>" + JSON.stringify(matchSets) + "</pre>");
document.write("<b>One possible formatted output:</b>");
var str = "<p>(Note that line numbers are 0-based...easily changed to 1-based if desired)</p>";
str += "<pre>";
matchSets.forEach(function(set, setNum, arr) {
str += "Matching code block #" + (setNum + 1) + ": lines " + set[0] + "-" + set[set.length - 1] + "<br />";
});
str += "</pre>";
document.write(str);

Categories

Resources