Write a function that takes in a string of one or more words, and returns the same string, but with all five or more letter words reversed. Strings passed in will consist of only letters and spaces. Spaces will be included only when more than one word is present.
The code I wrote reverses the whole string. I believe it is something with the if statement, it is not catching words greater than 5. My thinking when writing the code is to first convert the string into an array of words, use a for loop to run through the arrays and find the words that are greater than 5, then reverse the words greater than 5. I have found similar problems and this is how far I got searching for help. I'm stuck, any help would be greatly appreciated! I am new to coding and learning Javascript.
function spinWords(backward){
var sentence = "";
var separate = backward.split("");
for (var i = separate.length - 1; i >= 0; i--){
if (separate[i].length >= 1){
sentence += separate[i].split("").reverse().join("");
}
else {
sentence += "" + separate[i];
}
}
return sentence;
}
spinWords("Hey fellow warriors");
Write a function that takes in a string of one or more words, and returns the same string, but with all five or more letter words reversed. Strings passed in will consist of only letters and spaces. Spaces will be included only when more than one word is present.
The code I wrote reverses the whole string. I believe it is something with the if statement, it is not catching words greater than 5. My thinking when writing the code is to first convert the string into an array of words, use a for loop to run through the arrays and find the words that are greater than 5, then reverse the words greater than 5. I have found similar problems and this is how far I got searching for help. I'm stuck, any help would be greatly appreciated! I am new to coding and learning Javascript.
function spinWords(backward){
var sentence = "";
var separate = backward.split("");
for (var i = separate.length - 1; i >= 0; i--){
if (separate[i].length >= 1){
sentence += separate[i].split("").reverse().join("");
}
else {
sentence += "" + separate[i];
}
}
return sentence;
}
spinWords("Hey fellow warriors");
Share
Improve this question
asked Dec 30, 2018 at 23:01
xendezxendez
111 silver badge2 bronze badges
1
- Your problem statement isnt 100% clear. Are you saying you only want to reverse words that are 5 characters or more long? – Paul Rooney Commented Dec 30, 2018 at 23:06
2 Answers
Reset to default 4You might find the logic a lot easier if you used a regular expression to match 5 or more word characters in a row, and use a replacer function to reverse
them:
function spinWords(backward) {
return backward.replace(/\w{5,}/g, word => word.split('').reverse().join(''));
}
console.log(spinWords("Hey fellow warriors"));
To fix your code, you should split the input string (the variabla named backward
) by a space, not by the empty string, to get an array of words, then iterate through that array (starting from the beginning, not at the end), checking for each word's length
and concatenating with sentence
:
function spinWords(backward) {
var sentence = "";
var separate = backward.split(" ");
for (var i = 0; i < separate.length; i++) {
if (sentence) sentence += ' ';
if (separate[i].length >= 5) {
sentence += separate[i].split("").reverse().join("");
} else {
sentence += separate[i];
}
}
return sentence;
}
console.log(spinWords("Hey fellow warriors"));
Below is a discussion about a possible way to approach this problem. It is not what I would likely do for a question as simple as this, but it demonstrates a mindset that makes it easier to solve many problems, and to reuse your work as you do so.
Breaking the problem down
You want to do several things here, so it might help to break the problem down into steps.
At the core, you want to reverse certain words. So why not write a function to do only that? It should be simple. Here is one version:
const reverseWord = word => word.split('').reverse().join('')
reverseWord('word') //=> 'drow'
But you only want to do this to words whose length is at least five. Rather than rewriting our existing function, we can use it to write the more plex one:
const reverseIfGte5 = word => word.length >= 5 ? reverseWord(word) : word
reverseIfGte5('word') //=> 'word'
reverseIfGte5('supercalifragilisticexpialidocious')
//=> 'suoicodilaipxecitsiligarfilacrepus')
Note that here, we write reverseIfGte5
using reverseWord
. So our reverseWord
function is also available to be reused if we choose, but also so that each function is doing something simple.
We could now write spinWord
in terms of this function, via
const spinWords = sentence => sentence.split(/\s+/).map(reverseIfGte5).join(' ')
spinWords('Hey fellow warriors') //=> 'Hey wollef sroirraw'
This does what's expected. And it might be a good place to stop. However...
Do only one thing per function
Our spinWords
function above is responsible for two things:
- finding the words in the sentence
- applying our reversing function to each
Ideally, a function should be responsible for one thing an one thing only. (There is some judgment to be made in deciding how such responsibilities break down, of course.)
It might be useful to break this apart. We can do that in different ways. Here's a simple one:
const updateWords = wordFn => sentence => sentence.split(/\s+/).map(wordFn).join(' ')
const spinWords = updateWords(reverseIfGte5)
Now we have a reusable function, which we could use, for instance, with an obvious capitalizeFirstLetter
function like this:
const titleCase = updateWords(capitalizeFirstLetter)
titleCase('Hey fellow warriors') //=> 'Hey Fellow Warriors')
Fixing bugs
Our code now looks like this:
const updateWords = wordFn => sentence => sentence.split(/\s+/).map(wordFn).join(' ')
const reverseWord = word => word.split('').reverse().join('')
const reverseIfGte5 = word => word.length >= 5 ? reverseWord(word) : word
const spinWords = updateWords(reverseIfGte5)
These functions are ordered from the most likely to the least likely to be reusable.
Note that the total code here, when using such reusable functions, is a bit longer than this plain version:
const spinWords = sentence => sentence.split(/\s+/).map(
word => word.length >= 5 ? word.split('').reverse().join('') : word
).join(' ')
But our version has several advantages. Obviously, reusability is one. But another is that with the problem broken down into small pieces, if we find a problem, we know where look to for a solution.
And guess what, there is in fact a potential bug in this solution.
If we call this function with 'Hey fellow warriors'
, we return 'Hey wollef oirraw'
instead of the expected 'Hey wollef sroirraw'
. Our spacing is off.
Because this problem is broken down into distinct functions, there is no question about which function we need to change in order to fix this. Clearly it is the function responsible for pulling apart and putting back together the sentence, updateWords
. There is one simple fix to this, changing from
const updateWords = wordFn => sentence => sentence.split(/\s+/).map(wordFn).join(' ')
to
const updateWords = wordFn => sentence => sentence.split(' ').map(wordFn).join(' ')
but we might be better off using a variant of the answer from CertainPerformance, and rewriting it like this:
const updateWords = wordFn => sentence => sentence.replace(/\w+/g, wordFn)
This fixes the bug and actually handles more cases than we could have originally. (Note that it now deals with punctuation characters as well):
spinWords('Hey, fellow warriors!') //=> 'Hey, wollef sroirraw!'
The important point is that in order to fix our bug, we were able to isolate the function responsible and change it without changing anything else.
Working code
This is what we have arrived at:
const updateWords = wordFn => sentence => sentence.replace(/\w+/g, wordFn)
const reverseWord = word => word.split('').reverse().join('')
const reverseIfGte5 = word => word.length >= 5 ? reverseWord(word) : word
const spinWords = updateWords(reverseIfGte5)
console.log(spinWords('Hey, fellow warriors!'))
We have broken the problem down into two fairly reusable functions (reverseWord
and updateWords
), and two very specific to our problem (reverseIfGte5
and spinWords
.) Each has a well-defined responsibility, and they are easy to test in isolation.
Extending further
This is as far as I would likely go with such a function. But because my personal library often includes a function, when
, which is already a generalization of one of these, sometimes I might prefer to build on that:
const when = (cond, then) => val => cond(val) ? then(val) : val
const reverseIfGte5 = when(word => word.length >= 5, reverseWord)
I created when
because there are times when I want to use an altered version of my input if some condition is true about it and use it unaltered if the condition is false. That is exactly what reverseIfGte5
needs to do, so it's useful to build it on top of when
.
This is how utility libraries are built. Several specific problems have obvious connections and more general solutions to them are written to handle them. If these such solutions are generic enough, they are candidates for inclusion in your personal library or your team's library. And if they are useful to a wider audience, they might get included in a general-purpose utility library.
I'm one of the authors of Ramda, a utility library for functional programming in JS, and this is exactly how it has been built.