1
\$\begingroup\$

The task

is taken from leetcode

Write a function that takes a string as input and reverse only the vowels of a string.

Example 1:

Input: "hello"

Output: "holle"

Example 2:

Input: "leetcode"

Output: "leotcede"

Note: The vowels does not include the letter "y".

My first solution

/**
 * @param {string} s
 * @return {string}
 */
var reverseVowels = function(s) {
  const LEN = s.length;
  const str = [...s];
  const vowels = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'];
  const rev = [];
  for (let i = 0; i < LEN; i++) {
    if (vowels.includes(s[i])) { rev.push(s[i]); }
  }
  for (let i = 0; i < LEN; i++) {
    if (vowels.includes(str[i])) { str[i] = rev.pop(); }
  }
  return str.join('');
};

My second solution

/**
 * @param {string} s
 * @return {string}
 */
var reverseVowels = function(s) {
  if (s.length <= 1) { return s; }
  const sForward = [...s];
  const sBackward = [...s].reverse();
  const LEN = s.length - 1;
  let left = -1, right = -1;
  const VOWELS = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'];
  const findVowel = start => (x, i) => start < i  && VOWELS.includes(x);
  while(true) {
    left = sForward.findIndex(findVowel(left));    
    right = sBackward.findIndex(findVowel(right));
    if (left >= LEN - right || left === -1 || right === -1) { return sForward.join(''); }

    [sForward[left], sForward[LEN - right]] = [sBackward[right], sForward[left]];
  }
  return sForward.join('');
};

My third solution

/**
 * @param {string} s
 * @return {string}
 */
var reverseVowels = function(s) {
  if (s.length <= 1) { return s; }
  const str = [...s];
  let left = 0;
  let right = s.length - 1;
  const VOWELS = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'];
  while(left < right) {
    if (VOWELS.includes(str[left]) && VOWELS.includes(str[right])) {
      [str[right], str[left]] = [str[left], str[right]];      
      left++;
      right--;
    } else if (VOWELS.includes(str[left])) {
      right--;
    } else {
      left++;
    }
  }
  return str.join('');
};

My fourth solution

/**
 * @param {string} s
 * @return {string}
 */
var reverseVowels = function(s) {
  if (s.length <= 1) { return s; }
  const str = [...s];
  let left = 0;
  let right = s.length - 1;
  const map = new Map();
  map.set('a', true);
  map.set('e', true);
  map.set('i', true);
  map.set('o', true);
  map.set('u', true);
  map.set('A', true);
  map.set('E', true);
  map.set('I', true);
  map.set('O', true);
  map.set('U', true);
  while(left < right) {
    if (map.get(str[left]) && map.get(str[right])) {
      [str[right], str[left]] = [str[left], str[right]];      
      left++;
      right--;
    } else if (map.get(str[left])) {
      right--;
    } else {
      left++;
    }
  }
  return str.join('');
};

My fifth solution

/**
 * @param {string} s
 * @return {string}
 */
var reverseVowels = function(s) {
  if (s.length <= 1) { return s; }
  const str = [...s];
  let left = 0;
  let right = s.length - 1;
  const set = new Set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']);

  while(left < right) {
    if (set.has(str[left]) && set.has(str[right])) {
      [str[right], str[left]] = [str[left], str[right]];      
      left++;
      right--;
    } else if (set.has(str[left])) {
      right--;
    } else {
      left++;
    }
  }
  return str.join('');
};
\$\endgroup\$
1
\$\begingroup\$

I recommend using the Array.prototype methods.

These lend themselves to adopting a functional style of programming.

Functional programming is a lot easier to test, as a pure function always returns the same result and never mutate objects.

const vowels = ['a','e','i','o','u']; 
function vowelStringReverse(str) {
  
    //split the string into an array of chars, that includes their index position
    const strArray = str.split('').map((v,i) => ({
        value: v, 
        i: i, 
        isVowel: vowels.includes(v)
    })); 
    
    //For just the vowels, 
    //Create index position lookup, where the index positions are reversed
    const vowelsReverseIndexLookup = strArray.filter(v => v.isVowel)
    .reduce((acc, cur, i, array) => {
        return {
            ...acc, 
            [array[array.length-1-i].i]: cur.value
        }; 
    
    }, {}); 
    
    //Now iterate through the string, and look up the vowel from the lookup. 
    const strReversed = strArray.map((v,i) => {
        if (v.isVowel) {
            return vowelsReverseIndexLookup[i]
        }
        else {
            return v.value; 
        }
    }); 
    
    return strReversed.join(''); 
    
}


console.log(vowelStringReverse('hello')); 
console.log(vowelStringReverse('leetcode')); 

In this example I've given, if you wanted, you could then pull out the anonymous functions I've declared, and write tests for them:

for example:

const vowelsReverseIndexLookup = strArray.filter(v => v.isVowel)
.reduce(reverseIndexReducer, {}); 

//elsewhere: 

const reverseIndexReducer = (acc, cur, i, array) => {
    return {
        ...acc, 
        [array[array.length-1-i].i]: cur.value
    }; 

}; 
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.