CoderByte challenge - Letter Exchange - javascript

So I was doing some code challenge over on CoderByte and I can't get the Letter Exchange to work. The idea is to exchange all characters in a string with ones that are after them in alphabet. I tried with this code:
function LetterChanges(str) {
var string = "";
var i = 1;
var alp = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
var c = "";
for(i; i<=str.length; i++){
c = alp.charAt(alp.indexOf(str.charAt(i)));
string = string + c;
}
return string;
}
LetterChanges(readline());
But it won't work and I'm not sure why. It would be very helpful if you could point out my mistakes. :)
I know this is easy problem for lot of you guys out there, but I'm new to JavaScript.
Thanks.

For a start, your for loop needs some work. You should initialize i to 0, the first index, since accessing letters from a string is zero-based. The length of a string, however, is one-based, so if you were to try to access the character at str.length it will return null.
Try this:
function LetterChanges(str) {
var string = "";
var i = 0;
var upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var lower = "abcdefghijklmnopqrstuvwxyz";
var c = "";
for(i; i<str.length; i++){
if(str.charCodeAt(i) == str[i].toUpperCase().charCodeAt(0)){ // check if it's uppercase
c = upper[(upper.indexOf(str[i])+1)%upper.length]; // modulo for edge case if it's a zed
} else { // otherwise it's lowercase
c = lower[(lower.indexOf(str[i])+1)%lower.length]; // same thing as what's in the uppercase
}
string = string + c;
}
return string;
}
LetterChanges(readline());
Notice what I did, separating the upper and lower case letters. That's because I was assuming that you had to change the letter within the case. (so Z would become A, z would become a)

Related

Move string capital letters to front while maintaining the order (JavaScript)

This is my first post so I hope im doing this correctly.
I am taking a coding class and we were asked to make a piece of code that will ask for the input of a phrase, and will return in the console that phrase with the capital letters moved to the front, but still in the same order. Then print to the console this reordered phrase. (We aren't allowed to use arrays)
For example:
Inputting "HeLLoTherE" would return "HLLTEeoher"
However the problem is im having issues understanding how to write this code. How can I make the code select these capital letters and move them to the front? using .toUpperCase()? How can i make that select the letter and move it in front of the rest?
If someone could show me an example of how this is done and explain it a little i would greatly appreciate it :)
You might just start with a the most straight forward algorithm to get something working.
let value = "HeLLoTherE";
let result = "";
for (let char of value) {
if (char >= "A" && char <= "Z") {
result += char;
}
}
for (let char of value) {
if (char >= "a" && char <= "z") {
result += char;
}
}
console.log(result);
You could then consolidate the 2 loops by combining the conditions.
let value = "HeLLoTherE";
let upper = "";
let lower = "";
for (let char of value) {
if (char >= "A" && char <= "Z") {
upper += char;
} else if (char >= "a" && char <= "z") {
lower += char;
}
}
console.log(upper + lower);
Another way of solving this would be to use regex.
var value = "HeLLoTherE";
var upper = value.replace(/[^A-Z]*/g, "");
var lower = value.replace(/[^a-z]*/g, "");
console.log(upper + lower);
Well, you are not able to use arrays, which makes it a little bit difficult, however you can still do sommething.
Although I'm using a for loop, I'm not actually using arrays. Since strings allows the [] operator, you can use an index to select each character of the string and check if it's lowercase or uppercase.
In addition, you said you need to mantain the order of uppercase letters, so you couldn't just do newStr = upper + newStr, because it would revert the original order. So, I used the string.prototype.substring() to insert the uppercase character where it should be.
const str = "HeLLoTherE";
const moveUpperToFront = (target) => {
// Strings are immutable in js, so you cannot move one character
// to the front without using a new string.
let newStr = "";
// Number of uppercase letters that appeared.
// It's necessary because you need to mantain the original order
let upperNumber = 0;
// Iterate each character from beginning
for (let i = 0; i < str.length; ++i) {
// Is there an uppercase letter?
if (str[i].charCodeAt() >= 65 && str[i].charCodeAt() <= 90) {
newStr =
newStr.substring(0, upperNumber) +
str[i] +
newStr.substring(upperNumber, newStr.length);
++upperNumber;
}
// No uppercase letter?
else
newStr += str[i];
}
return newStr;
};
console.log(moveUpperToFront(str));
Following a solution which uses a for...of loop to iterate the input. It splits the input into capital and lowercase literals and then merges back together:
const exampleLiteral = 'HeLLoTherE';
const isUppercase = (literal) => literal === literal.toUpperCase() && literal !== literal.toLowerCase();
const prefixCapitalLetters = (literal) => {
let capitalLetters = '';
let lowerLetters = '';
for (let letter of literal) {
if(isUppercase(letter)) {
capitalLetters = capitalLetters.concat(letter);
continue;
}
lowerLetters = lowerLetters.concat(letter);
};
return capitalLetters+lowerLetters;
}
console.log(prefixCapitalLetters(exampleLiteral));
This is really not a very hard problem:
function rearrange(str) {
let result = "";
for (let c of str)
if (c >= 'A' && c <= 'Z')
result += c;
for (let c of str)
if (c < 'A' || c > 'Z')
result += c;
return result;
}
console.log(rearrange("Hello World, It Is A Beautiful Morning!"));
Find the upper-case characters, and add them to a result string. Then go back and find the other characters, and add them at the end. By looping through without any sorting, just simple iteration from start to finish, the order is preserved (other than the upper-case stuff).
The truly hard part of this would be coming up with a way to detect "upper-case" letters across all of Unicode. Some languages (well, orthographies) don't have the concept at all. JavaScript has ways that are more and less convenient to deal with that, but I suspect for the classroom material the OP has available so far, given the nature of the original question, such regex trickery would probably be inappropriate for an answer.
This answer tries to achieve the desired objective without using "arrays". It does use back-ticks, but that can be replaced with a simple string-concatenation if required.
Code Snippet
// move upper-case letters while
// keeping relative order same
const capsWithOrder = str => {
// initialize result variables
let capsOnly = "", restAll = "";
// iterate over the given string input
for (let i = 0; i < str.length; i++) {
// if character at index "i" is upper-case
// then, concatenate character to "capsOnly"
// else, concatenate to "restAll"
if (str[i] === str[i].toUpperCase()) capsOnly += str[i];
else restAll += str[i];
};
// after iterating over all characters in string-input
// return capsOnly concatenated with restAll
return `${capsOnly}${restAll}`;
};
console.log(capsWithOrder("HeLLoTherE"));
Explanation
Inline comments added in the snippet above.
Something like this
const string1 = 'HeLLoTherE'
const transform = string => {
const lower = string.split('').filter(c => c.charCodeAt() > 'a'.charCodeAt())
const upper = string.split('').filter(c => c.charCodeAt() < 'Z'.charCodeAt())
return [...upper, ...lower].join('')
}
console.log(transform(string1))
I think that must be work.
const sort = [
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
'abcdefghijklmnopqrstuvwxyz'.split('')
]
function listByValue(string) {
string = [...string];
let ret = [];
for (let i in sort)
ret = [...ret,...string.filter(e=>sort[i].includes(e))];
return ret.join('')
}

I need a regular expression to check the current index of array is not present in a specific range of numbers

I know that there is a lot of information on here about regex's, but I really cant seem to get this to work. I have a for loop, looping through an array. I want to see if the current index of the array is not equal to a group of numbers (32-64). I have declared a variable let patt which holds the regex that I think should work, but I cant figure out the syntax to check against it. I was sure it would be .match, but again, not sure how to word !.match
any advise, solutions or even a point in the direction of a good JS regex tutorial would be much appreciated!
class ShiftCipher{
constructor(shift){
this.shift = shift;
}
encrypt(string){
let up = string.toUpperCase(); //convert string to uppercase
let uni = [];
let newArr = [];
let i, j;
let patt = /[32-64]/g; //think this is wrong...
for(i = 0; i < up.length; i++){
uni.push(up.charCodeAt(i)) //push converted chars as unicodes to new array
if(uni[i] != 32){ // if unicode is 32 (space) leave as is. //I want a regex here for != unicode 32 - 64
uni[i] += this.shift; // shift unicode by parent class specification (shift)
}
}
for(j = 0; j < up.length; j++){
if(uni[j] > 90){ // if unicode is higher than 90(z)..
uni[j] -= 26; // loop back round starting at (a).
}
let text = String.fromCharCode(uni[j]); //convert from unicode to string
newArr.push(text); //push string to array
}
let final = newArr.join(''); //join array elements(as a string) and store in final
console.log(final);
}
}
const cipher = new ShiftCipher(2);
cipher.encrypt('I love to code z!');
I want to see if the current index of the array is not equal to a group of numbers (32-64)
Char codes are numbers. Try numerical comparisons.
for (i = 0; i < up.length; i++) {
if (up.charCodeAt(i) >= 32 && up.charCodeAt(i) <= 64) {
// ...
}
}
But technically, you can use regex too. Creating a character range from char codes works like this:
var patt = /[\x20-\x40]/; // hex 20 = decimal 32, hex 40 == decimal 64
for (i = 0; i < up.length; i++) {
if (patt.test(up.charAt(i)) {
// ...
}
}
Note that this uses .charAt().
The /[\x20-\x40]/ is interpreted as if you had written the actual characters, so in this case it's equivalent to /[ -#]/.
As your wraparound constant is 26, the length of the English alphabet, one could assume that you want to alter only the letters, and then the magical regexp is [A-Z]:
class ShiftCipher {
constructor(shift) {
this.shift = shift;
}
encrypt(string) {
return string.toUpperCase().replaceAll(/[A-Z]/g, char => {
let code = char.charCodeAt(0) + this.shift;
if(code>90)
code-=26;
return String.fromCharCode(code);
});
}
}
const cipher = new ShiftCipher(2);
console.log(cipher.encrypt('I love to code z!'));

How does the regular expression work in the function?

I've been dealing with an algorithm which takes a sequence of letters in alphabetic order, and if there is a character missing, it returns that character.
For example: fearNotLetter("abcdfg") would return "e".
My questions are:
What is the logic behind this solution?
Why and how regex is used here?
How does the condition in the for loop work?
function fearNotLetter(str) {
var allChars = '';
var notChars = new RegExp('[^'+str+']','g');
for (var i = 0; allChars[allChars.length-1] !== str[str.length-1] ; i++)
allChars += String.fromCharCode(str[0].charCodeAt(0) + i);
return allChars.match(notChars) ? allChars.match(notChars).join('') : undefined;
}
function fearNotLetter(str) {
var allChars = '';
var notChars = new RegExp('[^'+str+']','g'); //1
for (var i = 0; allChars[allChars.length-1] !== str[str.length-1] ; i++)//2
allChars += String.fromCharCode(str[0].charCodeAt(0) + i);
return allChars.match(notChars) ? allChars.match(notChars).join('') : undefined; //3
}
As the variable name says, it creates a negate of str which means
like !abcdfg
Loop, build the full correct string until the last
character of str, means full letters of the alphabet untill character g => abcdefg
Then it compares with match method of string the full text with the provided text:
dummy way you can think
abcdefg - abcdfg then you get e, if there are multiple characters are missing it concatenates with join method.

Find smallest substring containing a given set of letters in a larger string

Say you have the following string:
FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNT
LDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFY
FFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQ
XBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR
AMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR
I'm trying to find the smallest substring containing the letters ABCDA.
I tried a regex approach.
console.log(str.match(/[A].*?[B].*?[C].*?[D].*?[A]/gm).sort((a, b) => a.length - b.length)[0]);
This works, but it only find strings where ABCDA appear (in that order). Meaning it won't find substring where the letters appear in a order like this: BCDAA
I'm trying to change my regex to account for this. How would I do that without using | and type out all the different cases?
You can't.
Let's consider a special case: Assume the letters you are looking for are A, A, and B. At some point in your regexp there will certainly be a B. However, the parts to the left and to the right of the B are independent of each other, so you cannot refer from one to the other. How many As are matched in the subexpression to the right of the B depends on the number of As being already matched in the left part. This is not possible with regular expressions, so you will have to unfold all the different orders, which can be many!
Another popular example that illustrates the problem is to match opening brackets with closing brackets. It's not possible to write a regular expression asserting that in a given string a sequence of opening brackets is followed by a sequence of closing brackets of the same length. The reason for this is that to count the brackets you would need a stack machine in contrast to a finite state machine but regular expressions are limited to patterns that can be matched using FSMs.
This algorithm doesn't use a regex, but found both solutions as well.
var haystack = 'FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGRAMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR';
var needle = 'ABCDA'; // the order of letters doesn't matter
var letters = {};
needle.split('').forEach(function(ch) {
letters[ch] = letters[ch] || 0;
letters[ch]++;
});
var shortestSubstringLength = haystack.length;
var shortestSubstrings = []; // storage for found substrings
var startingPos = 0;
var length;
var currentPos;
var notFound;
var letterKeys = Object.keys(letters); // unique leters
do {
lettersLeft = JSON.parse(JSON.stringify(letters)); // copy letters count object
notFound = false;
posStart = haystack.length;
posEnd = 0;
letterKeys.forEach(function(ch) {
currentPos = startingPos;
while (!notFound && lettersLeft[ch] > 0) {
currentPos = haystack.indexOf(ch, currentPos);
if (currentPos >= 0) {
lettersLeft[ch]--;
posStart = Math.min(currentPos, posStart);
posEnd = Math.max(currentPos, posEnd);
currentPos++;
} else {
notFound = true;
}
}
});
if (!notFound) {
length = posEnd - posStart + 1;
startingPos = posStart + 1; // starting position for next iteration
}
if (!notFound && length === shortestSubstringLength) {
shortestSubstrings.push(haystack.substr(posStart, length));
}
if (!notFound && length < shortestSubstringLength) {
shortestSubstrings = [haystack.substr(posStart, length)];
shortestSubstringLength = length;
}
} while (!notFound);
console.log(shortestSubstrings);
Maybe not as clear as using regex could be (well, for me regex are never really clear :D ) you can use brute force (not so brute)
Create an index of "valid" points of your string (those with the letters you want) and iterate with a double loop over it getting substrings containing at least 5 of those points, checking that they are valid solutions. Maybe not the most efficient way, but easy to implement, to understand, and probably to optimize.
var haystack="UGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR";
var needle="ABCD";
var size=haystack.length;
var candidate_substring="";
var minimal_length=size;
var solutions=new Array();
var points=Array();
for(var i=0;i<size;i++){
if(needle.indexOf(haystack[i])>-1) points.push(i);
}
var limit_i= points.length-4;
var limit_k= points.length;
for (var i=0;i<limit_i;i++){
for(var k=i;k<limit_k;k++){
if(points[k]-points[i]+1<=minimal_length){
candidate_substring=haystack.substr(points[i],points[k]-points[i]+1);
if(is_valid(candidate_substring)){
solutions.push(candidate_substring);
if(candidate_substring.length < minimal_length) minimal_length=candidate_substring.length;
}
}
}
}
document.write('<p>Solution length:'+minimal_length+'<p>');
for(var i=0;i<solutions.length;i++){
if(solutions[i].length<=minimal_length) document.write('<p>Solution:'+solutions[i]+'<p>');
}
function is_valid(candidate_substring){
//verify we've got all characters
for(var j=0;j<candidate_substring.length;j++){
if(candidate_substring.indexOf(needle.charAt(j))<0) return false;
}
//...and verify we have two "A"
if(candidate_substring.indexOf("A")==candidate_substring.lastIndexOf("A")) return false;
return true;
}
Just had this problem in an interview as a coding assignment and came up with another solution, (it's not as optimal as the one above but maybe it's easier to understand).
function MinWindowSubstring(strArr) {
const N = strArr[0];
const K = strArr[1];
const letters = {};
K.split('').forEach( (character) => {
letters[character] = letters[character] ? letters[character] + 1 : 1;
});
let possibleSequencesList = [];
const letterKeys = Object.keys(letters);
for(let i=0; i< N.length; i++) {
const char = N[i];
if (new String(letterKeys).indexOf(char) !== -1) {
// found a character in the string
// update all previus sequences
possibleSequencesList.forEach((seq) => {
if(!seq.sequenceComplete) {
seq[char] = seq[char]-1;
seq.lastIndex = i;
// check if sequence is complete
var sequenceComplete = true;
letterKeys.forEach( (letter) => {
if(seq[letter] > 0) {
sequenceComplete = false;
}
});
seq.sequenceComplete = sequenceComplete
}
})
// create a new sequence starting from it
const newSeq = {
startPoint: i,
lastIndex: i,
sequenceComplete: false,
...letters
}
newSeq[char] = newSeq[char]-1;
possibleSequencesList.push(newSeq);
}
}
// cleanup sequences
let sequencesList = possibleSequencesList.filter(sequence => sequence.sequenceComplete);
let output = [];
let minLength = N.length;
// find the smalles one
sequencesList.forEach( seq => {
if( (seq.lastIndex - seq.startPoint) < minLength) {
minLength = seq.lastIndex - seq.startPoint;
output = N.substring(seq.startPoint, seq.lastIndex + 1);
}
})
return output;
}

In JavaScript, how would I retain the spaces in a sentence if I split it into an array, then join it?

I'm doing a coderbyte challenge with these instructions:
Using the JavaScript language, have the function LetterChanges(str) take the str parameter being passed and modify it using the following algorithm. Replace every letter in the string with the letter following it in the alphabet (ie. c becomes d, z becomes a). Then capitalize every vowel in this new string (a, e, i, o, u) and finally return this modified string.
My code does what the instructions say, but it doesn't retain the spaces of the original sentence.
function LetterChanges(str) {
var chars = str;
var newStr = [];
for (var i = 0; i < str.length; i++) {
if (/[a-y]/ig.test(chars[i])) {
newStr[i] = String.fromCharCode(chars[i].charCodeAt(0) + 1);
if (/[aeiou]/ig.test(newStr[i])) {
newStr[i] = newStr[i].toUpperCase();
}
} else if (/[z]/ig.test(chars[i])) {
newStr[i] = "A";
}
}
return newStr.join('');
}
LetterChanges("Argument goes here"); // result should be: BshvnfOUhpftIfsf
Is there a way to keep the spaces from the original string?
Add
else newStr[i]= chars[i];
To the end of the if/else

Categories