Complete the solution so that the function will break up camel casing, using a space between words.
Example: solution("camelCasing") == "camel Casing"
- input: string
- output: string with spaces between lowercase and uppercase letters
- methods:
reduce()
- determine where the uppercase letters exist
- turn string into array with
split()
- iterate through string array
- track index of uppercase letters
- depending on how many elements exist in the new array => return interpolation as a string, with each element inserted w/ space between
Refactor my initial solution with consideration for space/memory and time efficiency.
There are 6 versions of my solution; the progression of my solutions and thought process are documented below, beginning with my initial solution and ending with my final solution!
const solution = require('./index');
describe('Solution', () => {
it('should return formatted string', () => {
let input = 'camelCasing';
let output = 'camel Casing';
expect(solution(input)).toBe(output);
input = 'helloThereItIsNicole'
output = 'hello There It Is Nicole';
expect(solution(input)).toBe(output);
input = 'newyorkPizzaIsGreat';
output = 'newyork Pizza Is Great';
expect(solution(input)).toBe(output);
})
it('should account for capital at index 0', () => {
let input = 'BerneseMountainDogsAreAwesome';
let output = 'Bernese Mountain Dogs Are Awesome';
expect(solution(input)).toBe(output);
input = 'ILoveTheMountains';
output = 'I Love The Mountains';
expect(solution(input)).toBe(output);
})
})
const solution1 = (string) => {
const splitString = string.split('');
let capitalIndices = [];
splitString.forEach(el => {
if (el === el.toUpperCase()) {
capitalIndices.push(splitString.indexOf(el));
}
})
return splitString.reduce((output, el) => {
if (!capitalIndices.includes(splitString.indexOf(el))) {
output += el;
} else {
output += ' ' + el;
}
return output;
}, '')
}
solution1 runtime: 5.582 seconds
Next, I realize I should make use of the optional index
argument for the array iterator methods.
Also, splitString
makes for good readability, but takes up extra surface area in the function.
const solution2 = (string) => {
const splitString = string.split('');
let capitalIndices = [];
splitString.forEach((el, i) => {
if (el === el.toUpperCase()) {
capitalIndices.push(i);
}
})
return splitString.reduce((output, el, i) => {
return !capitalIndices.includes(i) ?
output += el : output += ' ' + el;
}, '')
}
solution2 runtime: 7.123 seconds
At this point, I am still using two array iterators, and I realize that I could perform the forEach()
logic inside of reduce()
.
const solution3 = (string) => {
let capitalIndices = [];
return string.split('').reduce((output, el, i) => {
if (el === el.toUpperCase()) {
capitalIndices.push(i);
}
return !capitalIndices.includes(i) ?
output += el : output += ' ' + el;
}, '')
}
solution3 runtime: 5.991 seconds - great improvement!
Now, I refactor specifically for space. That capitalIndices []
variable is an extra step I no longer need!
const solution4 = (string) => {
return string.split('').reduce((output, el, i) => {
if (el === el.toUpperCase()) {
output += ' ' + el;
} else {
output += el;
}
return output;
}, '')
}
solution4 runtime: 5.557 seconds
const solution5 = (string) => {
return string.split('').reduce((output, el, i) => {
return el === el.toUpperCase() ?
output += ` ${el}` : output += el;
}, '')
}
solution5 runtime: 5.2 seconds
In my final solution below, I add logic to account for a capital letter as the first letter in the string, so that a space is not automatically inserted into the string where it doesn't need to be!
I also change the ternary back to a regular if/else
block for readability.
const finalSolution = (string) => {
return string.split('').reduce((output, el, i) => {
if (el === el.toUpperCase() && i !== 0) {
output += ` ${el}`;
} else {
output += el;
}
return output;
}, '')
}
module.exports = finalSolution;
finalSolution runtime: 5.489 seconds
Big O: O(n)