Reverse The Vowels – iOS Engineering Interview Question

We’ve been asked to write an algorithm that reverses vowels in any given message. Assuming we are not allowed to use any native Swift String functions, How do we write such an algorithm🤔?
Here are some examples
Hello World = Hollo Werld
Welcome To RISING GALAXY = WAlcAmI TI RoSeNG GoLeXY
iOS Engineering = ieS enginEOring
Apple iPhone = epplo iPhenA
Reverse The Vowels = Revorse The Vewels
During some technical interviews, it’s not that uncommon to face challenges on String manipulation, such as being able to reverse character sequences.
This article is presenting a twist by reversing specific characters.
First, let’s break this challenge into pieces, and find out the correct approach. By rereading the challenge description and studying the provided examples. You’ll notice that we are facing two major challenges.
First, how do we reverse the series of vowels without using native Swift functions?. Second, how do we track the position of each vowels during loop iteration.
Let’s see whether we are up for this challenge.
STEP ONE – Create a class and name it ReverseVowel
1 2 |
class ReverseVowel { } |
STEP TWO – Inside the ReverseVowel class we should write a method that can tell us whether a given character is a vowel by returning a Boolean value. This method should have a single input parameter, which accepts a Character as a value.
1 2 3 4 5 6 |
private func isVowel( _ c: Character) -> Bool { switch c.lowercased() { case "a", "e", "i", "o","u" : return true default: return false } } |
STEP THREE – It’s time to write the magical code. Our method should have a single input parameter of type String and a returning value of type String as well.
Since the explanation can become a little confusing from this point. I’ll be commenting on each part of the code individually inside the syntax highlighter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
func reverseVowel(_ s: String) -> String { //Check whether the input value is not empty guard !s.isEmpty else { return s } //Converting the input value into an Array of Characters //We are going to be manipulating the order of this Array //That is why it should be a var/ variable and not a let/ constant var res: [Character] = Array(s) //Create a lookup array and remove any whitespaces let chars = Array(s.trimmingCharacters(in: .whitespacesAndNewlines)) //Create a reference, which tells us the number of input Characters let m = s.count //The pointer that tracks characters from the left side var i = 0 //The pointer that tracks characters from the right side var j = m - 1 //We will continue looping as long as i is less than j while i < j { //MARK: FIRST CHECK //It's time to call our isVowel(_:Character) method, //and check whether the current character //on the left side is a vowel //and the one on the right side is not a vowel if isVowel(chars[i]) && !isVowel(chars[j]) { //We should grab the character //from our lookup array on the 'j(th)' index //and insert it to the res array at the 'j(th)' index res.insert(chars[j], at: j) //Now, because we have injected a //new character into our 'res' array //the number of the 'res' characters has grown by 1 //that's why we should remove //the extra character on the (j + 1) index res.remove(at: j + 1) //Now, we should decrement 'j' by 1 //this will move this pointer 1 step to the left //as you can see, we are not touching the 'i' //because were able to found a vowel there, //so it should remain were it is //until we find a vowel on the left side j -= 1 //MARK: SECOND CHECK //if we didn't find a vowel on the left side //but we managed to find a vowel on the right side } else if !isVowel(chars[i]) && isVowel(chars[j]) { //We should grab the character //from our lookup array on the 'i'(th) index //and insert it to the 'res' array at the 'i'(th) index res.insert(chars[i], at: i) //Because we have injected a new character into our 'res' array //the number of the 'res' characters has grown by 1 //that's why we should remove //the character on the (i + 1) index res.remove(at: i + 1) //Next, we should increment 'i' by 1 //this will move this pointer 1 step to the right //as you can see, we are not touching 'j' this time. //Because found a vowel there, //we should let it stay where it is, //until we find a vowel on the right side i += 1 //MARK: THIRD CHECK //If we were able to find a vowel on both sides //Now, we should handle our character swapping } else if isVowel(chars[i]) && isVowel(chars[j]) { //We should grab the vowel from //our lookup on the 'i'(th) index //and insert it to the 'res' array at the 'j'(th) position res.insert(chars[i], at: j) //Again because we have injected a new //character into our 'res' array //the number of the 'res' characters has grown by 1 res.remove(at: j + 1) //We should grab the vowel from //our lookup on the 'j'(th) postion //and insert it to the 'res' array at the 'i'(th) position res.insert(chars[j], at: i) //Again, because we have injected //a new character into our 'res' array //the number of the 'res' characters has grown by 1 res.remove(at: i + 1) //Decrementing 'j' by 1 and moving it to the left j -= 1 //Incrementing 'i' by 1 and moving it to the right i += 1 //MARK: FINAL CHECK //If we didn't find vowels on both side } else { //We should grab the 'i'(th) character from the lookup array //and insert it at the 'i'(th) position of the 'res' array res.insert(chars[i], at: i) //Remove character on (i + 1) res.remove(at: i + 1) //Then, grab the 'j'(th) character from the lookup array //and insert it at the 'j'(th) index of the 'res' array res.insert(chars[j], at: j) //Remove character on (j + 1) res.remove(at: j + 1) //Decrement 'j' by 1 and move it to the right j -= 1 //Increment 'i' by 1 and move it to the left i += 1 } } //Everything is handled properly, //it's time to return the final result //by converting the 'res' array to a String return String(res) } |
FINAL SOLUTION – Clean Code + Unite Tests
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
import Foundation import XCTest class ReverseVowel { func reverseVowel(_ s: String) -> String { guard !s.isEmpty else { return s } var res: [Character] = Array(s) let chars = Array(s.trimmingCharacters(in: .whitespacesAndNewlines)) let m = s.count var i = 0 var j = m - 1 while i < j { if isVowel(chars[i]) && !isVowel(chars[j]) { res.insert(chars[j], at: j) res.remove(at: j + 1) j -= 1 } else if !isVowel(chars[i]) && isVowel(chars[j]) { res.insert(chars[i], at: i) res.remove(at: i + 1) i += 1 } else if isVowel(chars[i]) && isVowel(chars[j]) { res.insert(chars[i], at: j) res.remove(at: j + 1) res.insert(chars[j], at: i) res.remove(at: i + 1) j -= 1 i += 1 } else { res.insert(chars[i], at: i) res.remove(at: i + 1) res.insert(chars[j], at: j) res.remove(at: j + 1) j -= 1 i += 1 } } return String(res) } private func isVowel( _ c: Character) -> Bool { switch c.lowercased() { case "a", "e", "i", "o","u" : return true default: return false } } } class TestReverseVowels: XCTestCase { var sut: ReverseVowel! override func setUp() { super.setUp() sut = ReverseVowel() } override func tearDown() { sut = nil super.tearDown() } func testShort() { let input = "Hello" let testData = "Holle" let result = sut.reverseVowel(input) XCTAssertEqual(result, testData) } func testMedium() { let input = "Binary Search" let testData = "Banery Sairch" let result = sut.reverseVowel(input) XCTAssertEqual(result, testData) } func testLong() { let input = "United States Of America" let testData = "anited StAtOs ef americU" let result = sut.reverseVowel(input) XCTAssertEqual(result, testData) } } class TestObservation: NSObject, XCTestObservation { func testCase(_ testCase: XCTestCase, didRecord issue: XCTIssue) { assertionFailure(issue.description) } } let testObservation = TestObservation() XCTestObservationCenter.shared.addTestObserver(testObservation) XCTestSuite.default.run() |