I have a function that returns true if a character is a form of punctuation and I'm trying to write another function that accepts a string and removes the spaces and punctuation marks while calling the first function. I got most of it I think. But now I'm stuck. Any help is appreciated.
var isPunct = function(ch) {
if (ch = ch.match(/[,.!?;:'-]/g))
return true
else
return false
}
//B
var compress = function(s) {
var result = "";
//loop to traverse s
for (var i = 0; i < s.length; i++) {
if (!(isPunct(ch));
//(isPunct(s.charAt(i) || s.charAt(i) == " "));
//do nothing
else
result = result + !compress(i)
}
return result
}
Some issues:
The inner condition should in fact be the opposite: you want to do nothing when it is a punctuation character, i.e. you don't want to add it to the result. Only in the other case you want to do that.
The call !compress(i) is wrong: first of all that function expects a string, not an index, and it returns a string, not a boolean (so to perform ! on it). It seems like you want to call your function recursively, and although that is an option, you are also iterating over the string. You should do one of the two: recursion or iteration.
You reference a variable ch in the compress function which you have not defined there.
So, if you want to write compress the iteration way, change your code as follows:
var compress = function(s) {
var result = "", ch; // define ch.
//loop to traverse s
for (var i = 0; i < s.length; i++) {
ch = s[i]; // initialise ch.
if (!isPunct(ch)) result = result + ch; // only add when not punctuation
}
return result;
}
If on the other hand you want to keep your recursive call to compress, then you should do away with your for loop:
var compress = function(s) {
var result = "", ch, rest;
if (s.length == 0) return '';
result = compress(s.substr(1)); // recursive call
ch = s[0];
if (!isPunct(ch)) result = ch + result;
return result;
}
The function isPunct also has a strange thing happening: it assigns a boolean value to ch in the if expression. This does not make your function malfunction, but that assignment serves no purpose: the match method already returns the boolean you need for your if condition.
It is also not really nice-looking to first evaluate a boolean expression in an if to then return that same value in the form of false and true. This you can do by just returning the evaluated expression itself:
var isPunct = function(ch) {
return ch.match(/[,.!?;:'-]/g);
}
On a final note, you don't really need the isPunct function if you only use it in compress. The whole logic can be performed in one function only, like this:
let compress = s => s.replace(/[,.!?;:'-]/g,'');
// Demo:
console.log(compress('a,b,c')); // abc
If you prefer to keep isPunct and don't want to repeat the regular expression elsewhere, then you can do the replace like this:
let isPunct = ch => ch.match(/[,.!?;:'-]/g);
let compress = s => Array.from(s).filter(ch => !isPunct(ch)).join('');
// Demo:
console.log(compress('a,b,c')); // abc
Note how the use of ES6 arrow functions and ES5 Array methods makes the code quite lean.
Related
I recently had an interview where you had to recursively go over a string, and if it contained an AB || BA || CD || DC, it had to be deleted from the array. You would recursively go over this as deleting the CD from ACDBB would give you an AB which you would then have to delete to return a B as a string.
This is what I have, and when I test it out, I see it comes up with the right answer deep in the loops, but it never populates back to the top.
What am I missing?
const LETTERS = [/AB/g, /BA/g, /CD/g, /DC/g];
const stringGame = (string) => {
let newString = '';
if(string.length <= 1) return string;
LETTERS.forEach(regExToCheck => {
if(string.match(regExToCheck)) {
newString = string.replace(regExToCheck, '')
}
stringGame(newString);
})
return newString
}
// Expect answer: CAACC
console.log(stringGame('ABDCABCABAAABCCCD'))
Move the recursion,
return the recursion, and
Add an essential condition to end recursion:
const LETTERS = [/AB/g, /BA/g, /CD/g, /DC/g];
const stringGame = (string) => {
let newString = '';
if (string.length <= 1) return string;
LETTERS.forEach(regExToCheck => {
if (string.match(regExToCheck)) {
newString = string.replace(regExToCheck, '')
}
})
// Moved, returned, and ended recursion
return ('' === newString) ? string : stringGame(newString);
}
// Expect answer: CAACC
console.log(stringGame('ABDCABCABAAABCCCD'))
When recursing the function, i.e., when executing the function within itself, the return'ed value of that execution needs to be used, typically return'ed:
return stringGame(newString);
...in order for it to bubble back up the levels of recursion.
Also, since the replace() function is using a regex global replacement flag (g) all, for example, AB's, are being replaced in a single execution, so there's no need to recurse within the forEach() loop:
LETTERS.forEach(regExToCheck => {
if(string.match(regExToCheck)) {
newString = string.replace(regExToCheck, '')
}
stringGame(newString); // <-- double oops
})
Rather, recurse after the loop, AND return the value of the execution:
LETTERS.forEach(regExToCheck => {
if(string.match(regExToCheck)) {
newString = string.replace(regExToCheck, '')
}
})
return stringGame(newString);
One more principle of recursive functions is providing exit strategies—define what conditions to end the recursion, bubble back up, and return a final result.
The only exit condition provided is:
if (string.length <= 1) return string;
Surely if there's only one character left no match will be found—a proper time to end recursion. However, and more importantly, there also needs to be a path to exit when there are more than one characters but no matches can be found.
There are multiple ways to determine this but in the case provided in the question the most expedient is when after looping through the regex/replace routine no changes were made to the string. Since with every recursion the stringGame() function instantiates an empty string (newString) if, after looping through the regex/replace routine, newString is still an empty string, this indicates there were no matches found and it's time to end recursion:
if ( '' === newString ) {
return string;
}
else {
return stringGame(newString);
}
...otherwise, recurse.
The stringGame function doesn't have side effects, so the line in the loop here:
stringGame(newString);
doesn't do anything - you need to communicate the result of the recursive call back to the outer level.
A nicer way to approach this would be to combine the LETTERS into a single regular expression, and then to replace all matches with the empty string until the replacement produces no changes.
const LETTERS = [/AB/g, /BA/g, /CD/g, /DC/g];
const pattern = new RegExp(
LETTERS
.map(re => {
const str = String(re);
return str.slice(1, str.length - 2);
})
.join('|'),
);
const stringGame = (string) => {
const newString = string.replace(pattern, '');
return newString === string
? string
: stringGame(newString);
}
// Expect answer: CAACC
console.log(stringGame('ABDCABCABAAABCCCD'))
(If you want to use the global flag in the constructed pattern, you'll have to account for the .lastIndex)
I am new to JavaScript. I have created a indexof function in but it is not giving the correct output:
Question is:
/*
Implement a function called indexOf that accepts two parameters: a string and a character, and returns the first index of character in the string.
*/
This is my code:
function indexOf(string, character) {
let result = string;
let i = 0;
let output = 1;
while (i < result.length) {
if (result[i] === character) {
output = output + indexOf[i];
}
}
return output;
}
I want to know what i am doing wrong. Please Help.
You are making things a little harder than you need to. If you want to do this without calling the built-in indexOf(), which I assume is the point of the exercise, you just need to return from the function as soon as your condition matches. The instructions say "return the first index" — that's the i in your loop.
If you make it through the loop without finding something it's traditional to return -1:
function indexOf(string, character) {
let i=0;
while(i < string.length){
if(string[i] == character){ // yes? just return the index i
return i
}
i++ // no? increase i and move on to next loop iteration
}
return -1; // made it through the loop and without returning. This means no match was found.
}
console.log(indexOf("Mark Was Here", "M"))
console.log(indexOf("Mark Was Here", "W"))
console.log(indexOf("Mark Was Here", "X"))
Assuming from your question that the exercise is to only match the first occurrence of a character and not a substring (multiple characters in a row), then the most direct way to do it is the following:
const indexOf = (word, character) => {
for (let i = 0; i < word.length; i++) {
if (word[i] === character) {
return i;
}
}
return -1;
}
If you also need to match substrings, leave a comment on this answer if you can't figure it out and I'll help you along.
indexOf() is a built in method for strings that tells you the index of a particular character in a word. Note that this will always return the index of the FIRST matching character.-
You can write something like:
function indexOf(string, character){
return string.indexOf(character)
}
So if I were to use my function and pass in the two required arguments:
indexOf("woof", "o") //this would return 1
I try to check if a word (wordToCheck) only consists of letters from an array (letters) and also contains every letter in the array only as often (or rather not more times than they are in the array) as it actually is inside of the array.
Here are examples of what the desired function should return:
checkIfWordContainsLetters("google", ["a","o","o","g","g","l","e","x"]) === true
checkIfWordContainsLetters("google", ["a","o","g","g","l","e","x"]) === false
How can I make this code work?
function checkIfWordContainsLetters(wordToCheck, letters) {
var lettersToString = letters.toString();
var lettersTrimmed = lettersToString.replace(/,/gi, "?");
var regEx = new RegExp(lettersTrimmed, "gi");
if (wordToCheck.match(regEx)!== null) {
return true;
}
else return false;
}
You could use this ES6 function:
function checkIfWordContainsLetters(wordToCheck, letters){
return !letters.reduce((a, b) => a.replace(b,''), wordToCheck.toLowerCase()).length;
}
console.log(checkIfWordContainsLetters("google", ["a","o","o","g","g","l","e","x"]));
console.log(checkIfWordContainsLetters("google", ["a","o","g","g","l","e","x"]));
The idea is to go through each letter in the letters array, and remove one (not more!) occurrence of it in the given wordToCheck argument (well, not exactly in it, but taking a copy that lacks that one character). If after making these removals there are still characters left over, the return value is false -- true otherwise.
Of course, if you use Internet Explorer, you won't have the necessary ES6 support. This is the ES5-compatible code:
function checkIfWordContainsLetters(wordToCheck, letters){
return !letters.reduce(function (a, b) {
return a.replace(b, '');
}, wordToCheck.toLowerCase()).length;
}
console.log(checkIfWordContainsLetters("google", ["a","o","o","g","g","l","e","x"]));
console.log(checkIfWordContainsLetters("google", ["a","o","g","g","l","e","x"]));
As long as it is not the best solution for long strings for which using some clever regex is definitely better, it works for short ones without whitespaces.
function checkIfWordContainsLetters(word, letters){
word = word.toLowerCase().split('');
for(var i = 0; i < letters.length; i++) {
var index = word.indexOf( letters[i].toLowerCase() );
if( index !== -1 ) {
// if word contains that letter, remove it
word.splice( index , 1 );
// if words length is 0, return true
if( !word.length ) return true;
}
}
return false;
}
checkIfWordContainsLetters("google", ["a","o","o","g","g","l","e","x"]); // returns true
checkIfWordContainsLetters("google", ["a","o","g","g","l","e","x"]); // returns false
I have this little script that will check if one element of an array (arr[0]) is equal to the second element of the array (arr[1]). However when it checks the following array I would expect it to return false, yet it returns true. so my questions are, why does this return true, and how can I fix it to return false like expected?
function mutation(arr) {
var elem0 = arr[0].toLowerCase();
var elem1 = arr[1].toLowerCase();
for(var x=0; x < elem1.length; x++){
check = elem0.indexOf(elem1[x]);
if(check === -1){
return false;
}
return true;
}
}
mutation(["hello", "hey"]); //returns true
you place the return true to soon
you need to place it after the for statement like so
function mutation(arr) {
var elem0 = arr[0].toLowerCase();
var elem1 = arr[1].toLowerCase();
for(var x=0; x < elem1.length; x++){
check = elem0.indexOf(elem1[x]);
if(check === -1){
return false;
}
}
return true;
}
mutation(["hello", "hey"]); //returns false
You're looping over a characters in a string (see what elem1 actually is), and therefore you get true because the first character of hey, which is h, is indeed found within the string hello.
If you want to wait for it to finish iterating over the whole string, use a boolean flag, and then return the value of that flag when the iterations are over.
However, seems you just want to compare the two elements:
return elem0 === elem1;
I have this little script that will check if one element of an array
(arr[0]) is equal to the second element of the array (arr[1])
It returns true since e is in both the elements hello and hey
Your code is essentially iterating over all the characters in the string.
You need to simply check
function mutation(arr) {
return arr[0].toLowerCase() == arr[1].toLowerCase();
}
The expression of this question has some logical flaws or at least some lacking points. Such as the given condition means that all the items in the array must be equal. If this is the case then just one tiny piece of instruction is sufficient
myArray.every(e => e == myArray[0])
var a = [1,1,1,1,1,1,1,1],
b = ["hello", "hey"];
document.write("<pre> a array :" + a.every(e => e == a[0]) + "</pre>");
document.write("<pre> b array :" + b.every(e => e == b[0]) + "</pre>");
Looking at this piece of code:
for (var i = 0, f; f = families[i]; i++) {
}
I haven't actually seen a loop like this before and I want to be sure I understand it correctly.
Am I correct in assuming that if families.length == 2 that the 2nd part of the for line would return false on f = families[2]?
I would have thought it would need to be something like f == families[2] in order to return false.
f = families[i] is an expression that returns the value of families[i]. (It also has the side-effect of assigning that value to f)
If families.length === 2 then families[2] === undefined thus the expression returns undefined which is falsey and breaks the loop.
For more hacking fun you can turn
for (var i = 0, f; f = families[i]; i++) {
// body
}
into
for (var i = 0, f; f = families[i++]; /* body */);
You may have to string replace ; with , and string replace i with i-1. You also just murdered readability.
It should also be pointed out that the for loop is silly for readability.
Object.keys(families).forEach(function(key) {
var family = families[key];
/* body */
});
Is significantly more readable.
This looks like kind of a silly way of doing
for(var i in families) {
if (families.hasOwnProperty(i)) {
// do whatever you want with families[i]
console.log(families[i]);
}
}