how to i write my function in recursive way? - javascript

how do i write my function in recursive way? i found this task in my learnJS book, still can't figure it out even why should i do that.
btw function checks for polindrome
function clean(str) {
return str.toLowerCase().replace('ё', 'е').replace('ъ', 'ь').replace(/[^\w\s]|_/g, "").trim().replace(/\s+/g, " ");
}
function checkPalindrome(str) {
let cleanStr = clean(str);
for (let i = 0; i < cleanStr.length / 2; i++) {
if (cleanStr[i] !== cleanStr[cleanStr.length - 1 - i]) {
return false;
}
}
return true;
}

I'm not going to write the code for you, but I'll give you some pseudo-code logic that you can use to understand recursion.
function(input)
Check if input string length is less than or equal to 1 (explained below)
if it is then return true
Check if first and last characters are the same
if they are, strip the first and last characters off, pass this new string
to your function again, and return the result
if they are not, return false
So let's run through an example.
Example 1 (even length)
Input string is anna. Your code checks the string length, it wasn't <=1, so it continues in the function.
The first and last characters are the same. We strip those off, and pass nn into the function AGAIN (this is the recursion). Note that the first call hasn't returned yet.
The function checks the length of the input string nn, it's still >1, so continues. It checks first and last characters match, strips them off, and calls the function again with an empty string .
Now we are into the third call of the function. It's getting like Inception. On the third call, this blank string is <=1 length, so it returns true.
The second function call returns the value of the third (true).
The first function call returns the value of the second (true), and we're complete!
Example 2 (odd length)
Quicker now, let's look at a 5-letter palindrome civic.
The first call compares the start/end c, and continues on.
The second call compares the start/end i, and continues on.
The third call is checking if v is a palindrome. The length is less than or equal to one, and returns true!
Example 3 (invalid)
And an invalid palindrome dead.
The first call compares the start/end d and continues on.
The second call compares the start/end e and a and returns false.
The first call returns the value of the second (false), and it's an invalid palindrome.
Some final thoughts
You need to consider if/how you want to handle spaces (eg probably remove all spaces, Noel sees Leon) and capital letters (probably convert the entire sentence to lowercase). Even more complicated would be punctuation/non-English characters, but given this is a tutorial exercise, it's probably beyond the scope of the problem.

Related

Why slice(-x,0) doesn't produce expected string

If I have this:
console.log('12345'.slice(-3)); // I get '345'
but when I do:
console.log('12345'.slice(-3, 0)); // I get '' (empty string).
what is the reasoning behind this? It makes no sense to me. Is there a JS API that can wrap around strings like I expect here? (I was expecting '345' again.)
The second parameter is the index where the substring to be extracted ends (exclusive). If that index is before (or equal to) the start index (the first parameter), the result is the empty string.
As the docs say about the second parameter:
If end is positioned before or at start after normalization, nothing is extracted.
After normalization, .slice(-3) is equivalent to .slice(2), and the second parameter defaults to the length of the string, so
.slice(-3)
// equivalent to
.slice(2)
is like
.slice(2, 5) // take the substring from index 2 to index 5
and not
.slice(2, 0)

How do I replace a letter that is 15 chars ahead of each other?

I am trying to replace each character a user inputs that is 15 letters ahead in the alphabet. For example, if a user inputs the word A, then it would output "P".
the word "AB" would output "PQ" and so on with every word.
I've tried the following code but it is not working. I am thinking a loop may be the answer, but if anyone can think of some better ideas let me know.
alphabetArray = ["abcdefghijklmnopqrstuvwyxz"];
function jumpAhead15(aString){
aString.replace(alphabetArray[0][aString + 15]);
}
jumpAhead15("hi");
You could take the string as iterable and get an array of characters and replace any character by getting the index and the offset, adjusted by the remainder of the length of the alphabet.
The reminder operator % returns the rest of a division:
The remainder operator returns the remainder left over when one operand is divided by a second operand. It always takes the sign of the dividend.
The function is here to keep the index for the getting the character of alphabet in a valid range. For example by taking index 20 and the wanted shift of 15, you get 35 which is not an index of alphabet. by using the remainder operator you get the value of 9 which is the rest of 35 / 26 = 1 rest 9
Then return the character and join the array to a final string.
Method used:
Array.from, which takes two arguments, one iterable or an object with a length property and a mappring function, which is called for every element of the new array. (c is the first parameter of the callback and denotes here a single character)
arrow function as callback for Array.from
function jumpAhead15(aString) {
var alphabet = "abcdefghijklmnopqrstuvwyxz";
return Array
.from(
aString,
c => alphabet[(alphabet.indexOf(c) + 15) % alphabet.length]
)
.join('');
}
console.log(jumpAhead15("hi"));
console.log(jumpAhead15("ab"));
With alphabetArray as above, try:
aString.split("").map(letter => alphabetArray[(alphabetArray.indexOf(letter) + 15) % 26]).join("");
That is: convert the string to an array of 1-letter strings, replace each letter by the one 15 places further along (wrapping if necessary, hence the modulus operator %), then join the letters back together into one string.
If you find the answer from Nina hard to understand, then this one may not be for you either, but here is a function that is built on a more generic letter rotation scheme. It also handles differences in case:
const rotate = (configs) => (str) => str.split('').map(
c => configs.reduce(
(s, [letters, offset]) => {
const idx = letters.indexOf(s);
return idx < 0 ? s : letters[(idx + offset) % letters.length]
},
c
)
).join('')
const by15 = rotate([
[[...'abcdefghijklmnopqrstuvwxyz'], 15],
[[...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'], 15]
])
console.log(by15('ab')) //~> 'pq'
console.log(by15('aB')) //~> 'pQ'
console.log(by15('Hello, world!')) //~> 'Wtaad, ldgas!'
This uses the same basic technique with the modulus operator, modified slightly to return the original character if it's not in our list. We do this for each alphabet supplied, in this case, the upper- and lower-case letters. This only works if the different alphabets have no overlap. We pass the offset together with each alphabet, in case you want to, for instance, rotate digits by 3 and letters by 15. If you didn't want to do this, you could move the offset out of the configs parameter (in which case it might be called alphabets) and make the single offset a second parameter for rotate.
Note the structure: rotate is an arrow function which returns an arrow function. So to get a useful function out of it, you need to call it with the alphabet configuration. The function returned just takes a string and returns the rotated string.
This is not the most efficient solution, as you have to loop through each alphabet, even once you've found it. Since I wouldn't expect it to be used with many alphabets, this seems a good tradeoff for relatively clean code. But one could easily write a version which searched through until it found an alphabet that contained your character and then worked on that one. This is left as an exercise for the reader.

Retain Original Arguments Through Recursion (Javascript Preferred)

I'm doing a regex parse for an alphanumeric sort to compare two arguments. First I'm supposed to ignore the leading zeroes to make the token comparison. But if all tokens are equal, the tokens with the leading zeroes are smaller.
So I do a regex like /(\D|[1-9])/ and parse through it recursively (compare the first tokens, if equal, pass back new strings minus the leading equal tokens via regex.exec(arg1, arg2).
But if everything is equal, I want do to the whole thing again with regex /(\D|0*\d)/ to capture leading zeroes. I try to do this by returning a formula. So original becomes:
function (arg1, arg2, orig1, orig2);
and then I return
function compare(arg1new, arg2new, arg1 ,arg2)
but what happens is that on a loop iteration, arg1new becomes arg1 which gets returned back in the orig1 spot...so I lose the original function argument and instead get the same argument just one loop behind.
Is there a way to hold the original arguments through all recursions...and invoke only at the end?
Example:
function alphanumericLess(s1, s2, orig1, orig2) {
console.log(s1,s2);
if(s1.length<1 && s2.length>=1){ //if 1st string is shorter
return true;
}
var regex = /(\D|[1-9])(.*)/;
var leadS1 = regex.exec(s1); //parse string1 ignoring leading zeroes
var leadS2 = regex.exec(s2); //parse string2 ignoring leading zeroes
if(leadS1[1]==leadS2[1]){
console.log('if equals loop');
return alphanumericLess(leadS1[2],leadS2[2],s1,s2);
} else if(leadS1[1]<leadS2[1]){
return true;
} else return false; //here is where I want to then re-run the recursion with /(\D|0*\d)/ to capture the leading zeroes comparison scenario
}
You could solve this by creating a local functions inside your existing function, which will be called recursively instead of the existing one. Then you can call that local function in two different ways to launch the recursion twice.
There are also some issues with your current code:
A comparison function should better be in line with the signature that is required for a sort callback, i.e. it should return a negative number when the first argument should be sorted before the second, 0 when they are equal, and 1 if the first should be sorted after the second.
The regular expression only matches one character, but for a natural sort you should compare groups of digits with groups of digits, like in 12AB' and123AB`, the comparison should be between 12 and 123, not with 'A' and 3.
The ending condition at the start is not complete. You should also deal with the case when the second string is empty.
The code will fail if you pass a string that ends with a zero. In that case the regular expression will not yield a result when only that zero is left over. It would be a lot easier if you would preprocess the strings to remove leading zeroes, including those that occur at the very end. Then in a second instance you could restart the recursion without such replacement, but in both cases using a regular expression that matches all characters (including 0).
Here is a solution:
function alphanumericLess(s1, s2) {
// Use a local function for the recursion, so it can be called twice
function recurse(s1, s2) {
if (!s1.length || !s2.length) { // test ALL end conditions
// Don't return a boolean, but a numerical value
return s1.length - s2.length;
}
// Match multiple characters/digits as one part, deal with the 0 exclusion
// before calling the recurse function:
var regex = /(\D+|\d+)(.*)/;
var leadS1 = regex.exec(s1);
var leadS2 = regex.exec(s2);
if (leadS1[1] === leadS2[1]) { // Use strict comparison where possible
return recurse(leadS1[2], leadS2[2]);
} // After an if-return, else is not needed
// As parts can have different lengths, use appropriate comparison method,
// and return a number that represents the comparison result
if (isNaN(leadS1[1]) || isNaN(leadS2[1])) {
return leadS1[1].localeCompare(leadS2[1]);
}
// In the numerical case, if equal, give precedence to the
// longest representation (zero-padded):
return +leadS1[1] - +leadS2[1] || leadS2[1].length - leadS1[1].length;
}
// First call above function with leading zeroes removed.
// If that yields equality, call it again without such removal:
return recurse(s1.replace(/\b0+/g, ''), s2.replace(/\b0+/g, ''))
|| recurse(s1, s2);
}
console.log(alphanumericLess('012Ab3X0', '12Ab3X0')); // negative => order is correct
console.log(alphanumericLess('Ab30X0', 'Ab3X0')); // positive => order is reversed
console.log(alphanumericLess('Ab30X0', 'Ab30X0')); // zero => equal
// Use in sort: output is [ "001A", "1A", "2A", "B0999", "B0999X", "B999X" ]
console.log(['B999X', '1A', 'B0999', '001A', 'B0999X', '2A'].sort(alphanumericLess));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Remarks
The code for ignoring leading zeroes in the first sweep is left outside of the recursion: it is done with a one-shot removal of those leading zeroes. This way the recursive function is the same for both sweeps, just the arguments are different.
It is not efficient to execute a regular expression in each call of the recursive function. It would be more efficient to do a split of the strings before entering in the recursion. Of course, a non-recursive function would be even more efficient.
Finally, natural sort often also is case insensitive. You might want to put that logic in your function as well.
Some pseudocode that shows an example:
function keep (a,b) {
if (a == b) {
return b;
}
else {
return keep(a + 1, b);
}
}
keep(1, 5);
B is passed all the way up and after that, all the way down again...

JavaScript praseInt("3e3", 10) gives answer 3

My task to prompt for a number. And loop till its a number
do {
num=prompt ("Please enter a number:");
if (parseInt(num,10)) {
if (typeof num !=="number") {
if (!isNaN(num)) {
stop=1;
}
}
}
} while (stop != 1);
When I enter "3e3" it works. Y?
how do i fix the praseInt("3e3", 10)?
Check it with regular expression such as /^\d+$/.
if (/^\d+$/.test(num)) {
// it's an integer
} else {
// it's not an integer
}
parseInt will take the first characters of the string until it finds one that it's numeric (or reaches the end).
With that in mind, 3e3 reads the first 3 and discards the rest.
That said, your logic is flawed: parseInt returns the number, whereas you seem to be treating it like it were changing it.
That's because parseInt ignores anything after (and including) first invalid character (step 11.)
If you want to reject things like 3e3, then you can simply test whether the string contains decimals only by doing /^\s*\d+\s*$/.test(num).
If you want to process things like 3e3, then you can simply use unary + operator to convert a string to a number, something like +num. (This will accept strings like 4.2e+42 or 0x2A.)

Compare phone numbers in JavaScript

I have an array of phone numbers and I need to find if a particular phone number is in it.
What I tried doing at first was if(arr.indexOf(phoneNumber) != -1) { bla.. }. And it worked - sometimes.
I later discovered that since the number/s would arrive from different phones/entry forms, some people use country codes (like +1-xxx-xxx-xxxx), some wouldn't. Some use spaces as seperators and some just put in 10 digits in a row. In short - hell to compare.
What I need is an elegant solution that would allow me to compare, hopefully without having to replicate or change the original array.
In C++ you can define comparison operators. I envision my solution as something like this pseudo-code, hopefully using some smart regex:
function phoneNumberCompare(a, b) {
a = removeAllSeperators(a); //regex??
a = a.substring(a.length, a.length - 10);
b = removeAllSeperators(b); //regex??
b = b.substring(b.length, b.length - 10);
return (a < b ? -1 : (a == b ? 0 : 1)); //comaprison in C++ returns -1, 0, 1
}
and use it like if(arr.indexOf(phoneNumber, phoneNumberCompare) != -1)
Now, I know a solution like this construct does not exist in JavaScript, but can someone suggest something short and elegant that achieves the desired result?
As always, thanks for your time.
PS: I know indexOf() already has a second parameter (position), the above is just ment to illustrate what I need.
You really should sanitize all the data, both at collection and in the DB.
But for now, here's what you asked for:
function bPhoneNumberInArray (targetNum, numArray) {
var targSanitized = targetNum.replace (/[^\d]/g, "")
.replace (/^.*(\d{10})$/, "$1");
//--- Choose a character that is unlikely to ever be in a valid entry.
var arraySanitized = numArray.join ('Á').replace (/[^\dÁ]/g, "") + 'Á';
//--- Only matches numbers that END with the target 10 digits.
return (new RegExp (targSanitized + 'Á') ).test (arraySanitized);
}
How it works:
The first statement removes everything but digits (0-9) from the target number and then strips out anything before the last 10 digits.
Then we convert the array to be searched into a string (very fast operation).
When joining the array, we use some character to separate each entry.
It must be a character that we are reasonably sure would never appear in the array. In this case we chose Á. It could be anything that doesn't ever appear in the array.
So, an array: [11, 22, 33] becomes a string: 11Á22Á33Á, for example.
The final regex, then searches for the target number immediately followed by our marker-character -- which signals the end of each entry. This ensures that only the last 10 digits of an array's number are checked against the 10-digit target.
Testing:
var numArray = ['0132456789', "+14568794324", "123-456-7890"];
bPhoneNumberInArray ("+1-456-879-4324", numArray) // true
bPhoneNumberInArray ("+14568794324", numArray) // true
bPhoneNumberInArray ("4568794324", numArray) // true
bPhoneNumberInArray ("+145 XXX !! 68794324", numArray) // true !
bPhoneNumberInArray ("+1-666-879-4324", numArray) // false
You should sanitize both the input and all array values, to make sure they conform to the same ruleset.
Just create a function called sanitizePhonenumber, where you strip (or add, depending on your preferences) the country code and all other signs you dont want there.
After that you can just compare them as you are doing now.

Categories

Resources