Alberto Verissimo
Alberto Verissimo

Reputation: 27

Capitalize the vowels

I have been doing a challenge in which I have to create a function in Javascript which would change the letter position by one and vowels have to be capitalized, for instance "f" goes "g", "z" to "A" and "m" goes to "O". I did a big chunk of these steps but I am struggling with capitalizing the vowels. This is my code:

function LetterChanges(str) {
  var newString = [];

  for (var i = 0; i < str.length; i++) {
    if (str.charCodeAt(i) >= 97 && str.charCodeAt(i) <= 121) {
      newString.push(String.fromCharCode(str.charCodeAt(i) + 1));
    }
    // z to a
    else if (str.charCodeAt(i) === 122) {
      newString.push(String.fromCharCode(str.charCodeAt(i) - 57));
    } //spaces
    else if (str.charCodeAt(i) === 32) {
      newString.push(String.fromCharCode(32));
    } else {
      newString.push(String.fromCharCode(str.charCodeAt(i)));
    }
  }

  return newString.join("");
}

console.log(LetterChanges("fanzd times!"));

Upvotes: 0

Views: 882

Answers (5)

georg
georg

Reputation: 215029

You're not alone. Many people when confronted with a problem like this, start by writing complicated logic involving character codes, if statements etc. There's usually a much simpler way: write down "source" and "target" alphabets and write two lines of code that translates the input between them. If the result is not what you expect, just edit the target alphabet.

src = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
dst = 'bcdEfghIjklmnOpqrstUvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZA'

str = 'whatever!'
out = ''

for (c of str)
  out += dst[src.indexOf(c)] || c
  
console.log(out)
  

Upvotes: 3

Liam Gray
Liam Gray

Reputation: 1129

Character Ranges/Codes vs. Dictionary Mappings

Dealing with character ranges is quite confusing, the 97 <= str.charCodeAt(i) <= 121 makes very little sense without looking for ascii char code references. A cleaner solution is to build a dictionary that maps the letters to your desired result, such as {a: 'b', b: 'c', c: 'd', d: 'E', ...} which can be built very efficiently and cleanly — this is much easier to debug, and a much more readable/understandable solution.

Proposed Solution

As a modified version of one of my answers to another question (Replace letters in string with a dictionary array set in Javascript), you could instead do something similar to this if you're using Node.js, a JS precompiler, or don't care about old browsers and can use ES6:

class LetterChanges {
    constructor() {
        const alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];

        this.encryptionKey = {};

        // build encryptionKey = {a: 'b', b: 'c', c: 'd', d: 'E', ...}
        for (let i=0; i<alphabet.length; i+=1) {
            const j = (i + 1) % alphabet.length;

            this.encryptionKey[alphabet[i]] = alphabet[j].replace(/[aeiou]/, match => match.toUpperCase());
        }
    }

    /**
     * Encrypt some plaintext
     * @param plaintext
     */
    encrypt(plaintext) {
        return plaintext.split('').map(letter => this.encryptionKey[letter] || letter).join('');
    }
}

const cipher = new LetterChanges();
const plaintext = 'fanzd times!';
const encoded = cipher.encrypt(plaintext);

console.log(plaintext, encoded);  // "fanzd times!", "gbOAE Ujnft!"

Upvotes: 1

Nicole
Nicole

Reputation: 1707

For the special case "z" goes to "A", you are already treating that correctly by moving from lowercase to uppercase. You can treat the other vowels in the same manner, by specifically searching for the ASCII values of the preceding characters and changing them to the correct ASCII code for the uppercase vowel. Of course you need to do that distinction before the general case "a - z" in order for the code to work ;-)

Bottom line: if-else cascades need not have mutually exclusive conditions - the first match wins, and the others are skipped because of the "else".

Also, your cases for "32" and "all the rest" are identical, so you can combine them. And if you don't change the char, you can just use str[i] - no conversion required.

Upvotes: 0

jackreichert
jackreichert

Reputation: 1979

As far as I know there is no clever algorithmic way to see if a letter is a vowel. So for the check, you're going going to need to use a plain old indexOf ['a', 'e', 'i', 'o', 'u'].indexOf(c) !== -1.

I would place it after your other transformations in the loop as a separate condition on the newString at position i and perform the transformation if it's a vowel.

Upvotes: 0

user2267175
user2267175

Reputation: 593

You can just run a string.replace and check for vowels as following;

function LetterChanges(str) {
  var newString = "";

  for (var i = 0; i < str.length; i++) {
    if (str.charCodeAt(i) >= 97 && str.charCodeAt(i) <= 121) {
      newString += String.fromCharCode(str.charCodeAt(i) + 1);
    }
    // z to a
    else if (str.charCodeAt(i) === 122) {
      newString += String.fromCharCode(str.charCodeAt(i) - 57);
    } //spaces
    else if (str.charCodeAt(i) === 32) {
      newString += String.fromCharCode(32);
    } else {
      newString += String.fromCharCode(str.charCodeAt(i));
    }
  }


  return newString.replace(/[aeiou]/gi, function(a){return a.charAt().toUpperCase()});
}

console.log(LetterChanges("fanzd times!"));

Upvotes: 1

Related Questions