Taking a string and reversing certain words -- Javascript - javascript

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");

You 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 complex 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.

Related

How to use Regex to see if String matches another string partially (or completely)?

I am making a trivia system, and the variable triviaA is changed to the updated Answer every time there is a new question. I was wondering how I could use Regex to make it so that if triviaA = 'eagle' then if someone submitted the answer as eagle but with a small spelling mistake, the if statement would still return both the triviaA answer and the players answer return true.
I'm new to Regex, please excuse my mistakes.
You can dynamically build a regular expression that matches all possible "errors" (wrong/missing/extra letter) and apply it to the source:
function fuzzyContains(word, str) {
let r = [];
for (let i = 0; i < word.length; i++) {
// wrong letter
r.push(word.slice(0, i) + '[a-z]' + word.slice(i + 1));
// missing letter
r.push(word.slice(0, i) + '' + word.slice(i + 1));
// extra letter
r.push(word.slice(0, i) + '[a-z]' + word.slice(i));
}
return new RegExp(r.join('|')).test(str)
}
console.log(fuzzyContains('eagle', 'fly, ewgle, fly'))
console.log(fuzzyContains('eagle', 'fly, eagl, fly'))
console.log(fuzzyContains('eagle', 'fly, eaggle, fly'))
console.log(fuzzyContains('eagle', 'fly, eagly, fly'))
See https://norvig.com/spell-correct.html for other interesting ideas.
This would actually be quite difficult to do with RegEx. However, there's a concept in computing called "edit distance", which is a measure of how "similar" two strings are, and there are known algorithms to calculate that. Which means there are Node packages to calculate it :)
For instance, there's the aptly named Node package edit-distance: https://www.npmjs.com/package/edit-distance (Note: edit distance is sometimes called Levenshtein edit distance, named after the man who first studied it.)
I'll give you an example using that package; other packages may work somewhat differently to calculate the same thing.
There are three types of changes between two strings: an insertion, where a character is added in one that's not in the other; a deletion, where a character is removed in one that's there in the other; and an update/substitution, where a letter is changed between the strings. Using the edit-distance package, you define a function that assigns a cost to each of these types of changes. Then you call the package's levenshtein method, passing it the two strings and the three functions, and it returns an object with a distance property that is the sum score.
Assuming your cost functions return non-negative values, a score of 0 means the two strings are identical, and higher numbers mean they're more different. So you can use this to compare the entered value with the correct string and, if the result is lower than a certain threshold, accept it as "correct excluding typos".

Calculate the number of statements in a javascript function

I want to work out the number of statements inside a block of javascript code. This is to evaluate how short the code is for a programming challenge (if there's a better/easier way to evaluate code, I'm interested in hearing that too).
For the purposes of this evaluation, I would like to assume a statement is anything that is capable of performing an operation within it, for example:
let values = ['test 1', 'test 2'];
for(let i in values) {
let object = {
a: i%3,
b: Math.floor(i/3),
c: i*2
};
let another = {test: 0 || 4};
let something = values[i];
let otherSomething = getSomeValues(object[a], object[b]);
setSomeValues(object[a], object[c]);
for(let j = 0; j < 5; i++) if(i < j) break;
}
There's quite a lot of syntax to cover so ideally I would like to do this with a library if one is available - my Googling was unable to find anything very suitable.
I tried writing a regex to match all possible breaks between statements, but this is getting messy quickly:
[\n;]|\)[ \w]|[{,][\s\w]+:\s*|[{}]
Here's a link to the regexr I've been using. Breaking this down:
[\n;] - matches a newline or semicolon, the normal ways to start a new statement
\)[ \w] - matches statements following a closing bracket, e.g. if (something) return;
[{,][\s\w]+:\s* - matches the key of a key-value pair in an object
[{}] - matches opening and closing brackets of blocks
I also remove any zero-length matches as statements cannot be empty.
As I've said, ideally I would prefer a library but I wanted to ask for some opinions on my approach and if I've got most of the edge cases.
Since you are trying to understand a particular chunk of code and not building a library, you should check out astexplorer.net
Here is a link that displays a nicely parsed tree and if desired, you can configure the example to use alternative parsers (babel, acorn, eslint, etc).

If the execution of the bulk of a function is conditional on the input, which of these is a better way to implement?

I'm wondering which of these is better and why. I often encounter situations in my daily work where I'm like "This algorithm works so long as the input isn't empty" or something like that. I usually just return early because, for some reason, the idea of wrapping almost the entirety of a function in an if conditions seem wrong to me. However, I know that some religions don't believe in early return statements.
Example:
(1)
function combine ( strings , separator )
{
if ( strings.length > 0 )
{
retstring = strings[0];
for ( var i = 1; i < strings.length; ++ i )
retstring += (separator + strings[i]);
}
return retstring;
}
(2)
function combine ( strings , separator )
{
if (strings.length === 0) return undefined;
retstrings = strings[0];
for ( var i = 1; i < strings.length; ++ i )
retstring += (separator + strings[i]);
return retstring;
}
So which is better to go with in such situations?
I'd say that neither is "better"; it's subjective.
And, unlike many subjective programming choices, this one isn't just a matter of personal preference. Rather, I think a good programmer will use both patterns, choosing which one based on what they want to express.
Pattern #1 says "if X do Y". Pattern #2 says "If !X, don't do anything else." Admittedly, those two are equivalent to any browser running your code.
But, to a human reading your code (eg. such as a co-worker who has to modify it) each pattern suggests different things about what is going on. Thus, my recommendation would be to try and determine which of the two patterns best describes what you are trying to communicate, and use that.
For instance, many functions have "if this isn't relevant logic", and that is best expressed with pattern #2:
function doStuffIfLoggedIn(user) {
if (!user.isLoggedIn()) return;
doStuff();
}
But it's also fairly common to do something if a particular option is provided, and that fits better with the first pattern:
function format(word, capitalize) {
if (capitalize) {
word = string.toUpperCase();
}
returns word;
}
If either is equally valid (and I find this happens fairly often) then it does come down to a matter of preference. Personally, in those "either is valid" cases I opt for #2; all else being equal it results in less indentation, which I (subjectively) find easier to read.
But really, the important thing (IMHO) is to think about how your code will look to the person who comes after (and that might even be you, a year later when you've forgotten why you wrote it that way). The browser doesn't care either way, and your co-workers will be able to understand either one, but using the one that best represents the situation can offer a critical clue about the code's function to whoever reads it later.
EDIT
To your point about:
some religions don't believe in early return statements
I think the idea there is that multiple return statements can make the code more complicated. When your function has lots of ways of exiting it can become hard to understand its logic, because to interpret a latter part, you have to reason through whether any of the earlier parts prevented you from getting there.
However, the Stack Overflow consensus is that, while it's a good idea to avoid excessive return statements, using a few properly can make your code more readable, and thus are a good idea.
See:
Should a function have only one return statement?
There is a built-in array method that does what your functions do: join()
function combine(strings, separator) {
return strings.join(separator);
}
console.log(combine(['this','is','a','test'], '...')); //this...is...a...test
But if join() didn't exist, I'd recommend a variation on your first code. You don't have to explicitly return undefined. If you don't include a return statement, the function will automatically return undefined:
function combine(strings, separator) {
if (strings.length) {
var retstring = strings[0];
for (var i = 1; i < strings.length; ++i)
retstring += (separator + strings[i]);
return retstring;
}
}
console.log(combine(['this','is','a','test'], '...')); //this...is...a...test
console.log(combine([], '...')); //undefined

Javascript Unnecessarily Compact Array Operations

So, for my own knowledge, and because I love looking at things from a different perspective...
I have the following bite of Javascript code, which for this problem's intents and purposes will only ever receive strings formatted like this: "wordone wordtwo":
function inName(inputName) {
return inputName.split(" ")[1].toUpperCase();
}
However, this only returns half of what I want ("WORDTWO"). I desire to return the original string with a single change: the space-separated second word returned through the toUpperCase(); and then re-concatenated to the untouched first word.
I also want to unnecessarily run all of the operations on the return line. My brain says this is possible, given how as the compiler reads the line from left to right and makes adjustments to the available member functions based on what has resolved. Also everything in Javascript is an object, correct?
Help me out for my own curiosity's sake, or bash me over the head with my own misconceptions.
Here is a solved version of the above question using 'normal' statements:
function inName(inputName) {
var nameArray=inputName.split(" ");
nameArray[1]=nameArray[1].toUpperCase();
return nameArray.join(" ");
}
One line with substr, indexOf and a variable on the fly ;-)
function inName(inputName) {
return inputName.substr(0, (index = inputName.indexOf(' '))) + inputName.substr(index).toUpperCase();
}
Here's another option which avoids the regular expression:
function inName(inputName) {
return inputName.split(' ').map(function(v,i){return i?v.toUpperCase():v;}).join(' ');
}
This does the same split as the original code, then maps the parts to a function which returns the value at index 0 unchanged but the value at index 1 in upper case. Then the two results are joined back together with a space.
As others have said, a longer, clearer version is better in practice than trying to come up with a clever one-liner. Defining a function inside the return statement feels like cheating anyway ;-)
Something like this almost seems like it belongs on Code Golf, but here's my take:
function inName(inputName) {
return inputName.replace(/ .*/,function(m) {return m.toUpperCase();});
}
Interesting. Here is my take on the problem
function justDoIt(str){
return [str = str.split(" ") , str.pop().toUpperCase()].join(" ");
}
Creates a new array, str is split and reassigned as an array, and the first item of the new array, then the second new array item pops the last word, makes it uppercase, puts it into the new array. Then joins the array [["wordOne"],"WORDTWO"].join(" ")

How can I create (in Javascript), a tool to identify the longest repeated pattern in a series of numbers?

Alright, long story short, what I overall am attempting to do, is test the level of randomness in a series of multiple thousands of " previously generated seemingly "random" numbers.
I've already written something that will test for the probability of numbers with great success, however, the next step is identifying repeating or recurring patterns.
I'd prefer to get this part done in javascript, so as to avoid having to teach myself another language for the time being.
Now, obviously, I could just use regex and punch in some random sequences myself, but that is not ideal, and would take an infinite amount of time to get the results I'm after.
Ah, I missed a number of your comments above. I believe this is what you're looking for:
function findLongestMatch(StringOfNumbers) {
var matches = StringOfNumbers.match(/(.{2,})(?=.*?\1)/g);
if (!matches) { return null; }
var longestMatch = matches[0];
var longestMatchLength = longestMatch.length;
for (matchIndex = 1; matchIndex < matches.length; matchIndex++) {
if (matches[matchIndex].length > longestMatchLength) {
longestMatch = matches[matchIndex];
longestMatchLength = longestMatch.length;
}
}
return longestMatch;
}
It'll be slow, but it'll get the job done.

Categories

Resources