Retain Original Arguments Through Recursion (Javascript Preferred) - javascript

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...

Related

Removing a String from an Array, but adding the Number amount back in Javascript

I am trying to create a FEN notation converter for chess. I've decided the best way to go about this is to remove any unnecessary values from the string that I don't need. Here is what it looks like.
rnbqkbnr/pppppppp/8/
These are all strings in my code. I've already found solutions for the end of each file which is represented by the / in the string. But my new problem is taking away the number values and adding back in blank squares to cover them.
When you see a number in FEN, that is essentially how many empty squares on the Chess table there are until another piece. My idea once again, to dumb it down for myself, was to convert these numbers like 8 into this 11111111 a series of numbers representing each square that would have to be empty.
While in practice, I assumed that I would just be able to splice the 8 out and just start filling up that index onwards with 1, this seems to be viable option, but unfortunately is quite buggy when moving the number around to different places.
Would anyone have a better option for this? Yes I know that there is already libraries that accomplish this, but what's the fun of coding if you don't attempt to reinvent the wheel a couple times.
correctFen function - maps over the string looking for any numbers
const correctFen = () => {
newFen.map((pos, index) => {
if (Number(pos) && pos !== '/'){
for (let i = 0; i < Number(pos); i++) {
console.log('firing')
setNewFen(addAfter(newFen, index + i, '1'))
}
}
})
console.log(newFen)
figureOutPos()
}
after looking at this, it's not really removing the index that I'm wanting, could be a problem here, it's adding the 1s after the last /
function addAfter(array, index, newItem) {
return [
...array.slice(0, index),
newItem,
...array.slice(index)
];
}
Looks like you're modifying newFen from the map callback. This would indeed mess up indices because everything after the current index shifts around.
Instead, return the new character(s) from the map callback. In most cases this will be the original character, but if it's a digit, you'll return a string of multiple characters. Then join the array together into a string again.
Something like this:
// Assuming newFen is a string in some outer scope.
const correctFen = () => {
const correctedFen = newFen
// Split the string into an array of single characters.
.split('')
// Apply a function to each character and gather the results in an array.
.map((char, _index) => {
// Try to parse `char` as an integer.
const number = parseInt(char)
if (isNaN(number)) {
// `char` is not a digit. Return the character itself.
return char
} else {
// `char` is a digit. Return a string of that many 1s.
return '1'.repeat(number)
}
})
// Join the array back into a string.
.join('')
setNewFen(correctedFen)
}

how to i write my function in recursive way?

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.

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.

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.)

Convert an entire String into an Integer in JavaScript

I recently ran into a piece of code very much like this one:
var nHours = parseInt(txtHours);
if( isNaN(nHours)) // Do something
else // Do something else with the value
The developer who wrote this code was under the impression that nHours would either be an integer that exactly matched txtHours or NaN. There are several things wrong with this assumption.
First, the developer left of the radix argument which means input of "09" would result in a value of 0 instead of 9. This issue can be resolved by adding the radix in like so:
var nHours = parseInt(txtHours,10);
if( isNaN(nHours)) // Do something
else // Do something else with the value
Next, input of "1.5" will result in a value of 1 instead of NaN which is not what the developer expected since 1.5 is not an integer. Likewise a value of "1a" will result in a value of 1 instead of NaN.
All of these issues are somewhat understandable since this is one of the most common examples of how to convert a string to an integer and most places don't discuss these cases.
At any rate it got me thinking that I'm not aware of any built in way to get an integer like this. There is Number(txtHours) (or +txtHours) which comes closer but accepts non-integer numbers and will treat null and "" as 0 instead of NaN.
To help the developer out I provided the following function:
function ConvertToInteger(text)
{
var number = Math.floor(+text);
return text && number == text ? number : NaN;
}
This seems to cover all the above issues. Does anyone know of anything wrong with this technique or maybe a simpler way to get the same results?
Here, that's what I came up with:
function integer(x) {
if (typeof x !== "number" && typeof x !== "string" || x === "") {
return NaN;
} else {
x = Number(x);
return x === Math.floor(x) ? x : NaN;
}
}
(Note: I updated this function to saveguard against white-space strings. See below.)
The idea is to only accept arguments which type is either Number or String (but not the empty string value). Then a conversion to Number is done (in case it was a string), and finally its value is compared to the floor() value to determine if the number is a integer or not.
integer(); // NaN
integer(""); // NaN
integer(null); // NaN
integer(true); // NaN
integer(false); // NaN
integer("1a"); // NaN
integer("1.3"); // NaN
integer(1.3); // NaN
integer(7); // 7
However, the NaN value is "misused" here, since floats and strings representing floats result in NaN, and that is technically not true.
Also, note that because of the way strings are converted into numbers, the string argument may have trailing or leading white-space, or leading zeroes:
integer(" 3 "); // 3
integer("0003"); // 3
Another approach...
You can use a regular expression if the input value is a string.
This regexp: /^\s*(\+|-)?\d+\s*$/ will match strings that represent integers.
UPDATED FUNCTION!
function integer(x) {
if ( typeof x === "string" && /^\s*(\+|-)?\d+\s*$/.test(x) ) {
x = Number(x);
}
if ( typeof x === "number" ) {
return x === Math.floor(x) ? x : NaN;
}
return NaN;
}
This version of integer() is more strict as it allows only strings that follow a certain pattern (which is tested with a regexp). It produces the same results as the other integer() function, except that it additionally disregards all white-space strings (as pointed out by #CMS).
Updated again!
I noticed #Zecc's answer and simplified the code a bit... I guess this works, too:
function integer(x) {
if( /^\s*(\+|-)?\d+\s*$/.test(String(x)) ){
return parseInt(x, 10);
}
return Number.NaN;
}
It probaly isn't the fastest solution (in terms of performance), but I like its simplicity :)
Here's my attempt:
function integer(x) {
var n = parseFloat(x); // No need to check typeof x; parseFloat does it for us
if(!isNaN(n) && /^\s*(\+|-)?\d+\s*$/.test(String(x))){
return n;
}
return Number.NaN;
}
I have to credit Šime Vidas for the regex, though I would get there myself.
Edit: I wasn't aware there was a NaN global. I've always used Number.NaN.
Live and learn.
My Solution involves some cheap trick. It based on the fact that bit operators in Javascript convert their operands to integers.
I wasn't quite sure if strings representing integers should work so here are two different solutions.
function integer (number) {
return ~~number == number ? ~~number : NaN;
}
function integer (number) {
return ~~number === number ? ~~number : NaN;
}
The first one will work with both integers as strings, the second one won't.
The bitwise not (~) operator will convert its operand to an integer.
This method fails for integers bigger which can't be represented by the 32bit wide representation of integers (-2147483647 .. 2147483647).
You can first convert a String to an Integer, and then back to a String again. Then check if first and second strings match.
Edit: an example of what I meant:
function cs (stringInt) {
var trimmed = stringInt.trim(); // trim original string
var num = parseInt(trimmed, 10); // convert string to integer
newString = num + ""; // convert newly created integer back to string
console.log(newString); // (works in at least Firefox and Chrome) check what's new string like
return (newString == trimmed); // if they are identical, you can be sure that original string is an integer
}
This function will return true if a string you put in is really an integer. It can be modified if you don't want trimming. Using leading zeroes will fail, but, once again, you can get rid of them in this function if you want. This way, you don't need to mess around with NaN or regex, you can easily check validity of your stringified integer.

Categories

Resources