Remove Whitespace from Start and End - alternate solution mysteriously fails test

I have an alternate solution for the problem Remove Whitespace from Start and End that seems like it works—my solution returns “Hello, World!” with no spaces according to console.log()—but it fails the test. And if I test the “Hello, World!” that I get for equality against the “Hello, World!” returned by the official solution, the test returns false. So they look equal, but somehow they are not equal.

Here is what I used to test this, with my solution at the top, the official solution at the bottom, and some console.log() statements and equality tests built in (you can try a live version at this Repl.it page:

// My solution - fails test
let hello = "   Hello, World!  ";
let wsRegex = /^(\s*)(.+)(\s*)$/; 
let result = hello.replace(wsRegex, "$2"); 
console.log("my result = " + result) // prints Hello, World!

// FreeCodeCamp solution - passes test
let hello2 = "   Hello, World!  ";
let wsRegex2 = /^\s+|\s+$/g;
let result2 = hello2.replace(wsRegex2, "");

console.log("FCC result = " + result2) // also prints Hello, World!

console.log("testing loose equality: " + (result == result2)); // returns FALSE - why?
console.log("testing strict equality: " + (result === result2)); // returns FALSE - why?

Can anyone explain what is going on here?

2 Likes

you are using an if statement here so it can only match one or the other, but your answer needs to match both the start spaces and the end spaces

You’re not removing the trailing whitespace.

4 Likes

yeah you were right, now your gonna have to explain to me how the or statement matches both sides lol, cuz i always throught it as one or the other, sorry im not that clued up on regex but i always thought it meant one or the other just like if i use something like this const name = 'bob' || 'sam' its only gonna be one of them? or am i wrong? how can both sides be true? i dont see how this is possible without a loop, i would love to know how this works please @colinthornton is it just one big loop that runs untill it all matches? regex is so hard to understand

4 Likes

@biscuitmanz The regex you posted is from the solution that passes, I was showing that @bhagerty 's solution doesn’t pass the requirements as he expected.

/^\s+|\s+$/g

The above regex matches

  • ^\s+ one or more whitespace characters at the beginning of a string
  • | OR
  • \s+$ one or more whitespace characters at the end of a string
  • g globally, can make multiple matches (this is why it matches both the leading and trailing whitespace, and the replace method in the solution replaces those matches with empty strings, ie, removing them).

I like using this tool when working with regular expressions.

https://regexr.com/5b2o7

23 Likes

omg i forgot about the global, thanks mate i get whats going on now, i can be really dumb sometimes man, thanks for the explanation.

hey that page is pretty cool man even tells ya the error thanks.

2 Likes

this is greedy, it’s taking as much as possible while making sure that also the next group match - as the next group is satisfied with also 0 characters, the .+ match till the end of the string

if you want to use this, just return to the concept of greedy and lazy matching and how to fix that

10 Likes

Thank you! Now I get it.

1 Like

Thank you! That makes sense.

1 Like

In case you’re interested, here’s the regex I used to solve the problem after your comments:

/^(\s*)(.+[^\s*$])(\s*)$/

This is more complicated than the official solution, but it conceptually makes sense to me and uses the concepts of collection groups from the previous exercises. Basically, this now has three parts: (1) ^(\s*) says “match whitespace (if any) at the beginning”; (2) (.+[^\s$]) says “match everything in the middle, up until you get to trailing whitespace (if any), which does not match”; and (3) (\s*) says “match trailing whitespace (if any).” Then the expression is rewritten keeping just the middle capture group, i.e., everything but the trailing and leading whitespace.

Thanks again @colinthornton @biscuitmanz and @ilenia for your helpful comments!

9 Likes

this could have just been (.+?) to make it lazy matching

but anyway, good job in solving it!

6 Likes

This version works:

let hello = " Hello, World! “;
let wsRegex = /^\s+(.*?)\s+$/; // Change this line
let result = hello.replace(wsRegex,”$1"); // Change this line

Here is the solution I found.
Compared to the first post, it is slightly different but really close.
And it passed all the tests:

let hello = "   Hello, World!  ";
let wsRegex = /(^\s*)(.*\S)(\s*$)/; // Change this line
let result = hello.replace(wsRegex, "$2"); // Change this line

And, what I believe my code says:

  1. let wsRegex = /(^\s*)(.*\S)(\s*$)/ :
  • (^\s*): 0 or more whitespaces at the beginning;
  • (.*\S): all characters (between) and the last one is not a whitespace;
  • (\s*$): 0 or more whitespaces at the end.
  1. let result = hello.replace(wsRegex, "$2"): just keep the second group (all characters (between) and the last one is not a whitespace).
5 Likes

That was the solution I was trying, I realised the $2 is capturing the trailing white space, and then reinserting it when we use the replace function.

So I did the following

let wsRegex = /\s+(.*\S)\s+$/; // Change this line
let result = hello.replace(wsRegex, "$1"); // Change this line

Basically the (.*) like your (.+) was picking up the whitespace also, and storing it in the $2 variable. What the \S (capital S) did was the same as [^\s].

1 Like

But then wont that just stop after it matched the words? I thought the dot captures all, wouldn’t that include the spaces?

I have always had a hard time with regex. So please excuse the ignorance.

that’s why you use lazy matching, in that way it captures enough to satisfy the pattern but the least number of characters possible, meaning the spaces are left for the other capture groups

1 Like

Hi.
Here is my solution:

let hello = "   Hello, World!  ";
let wsRegex = /(\S+\s)*\S+/; // Change this line
let result = hello.match(wsRegex)[0]; // Change this line

as you can see there is no need for the replace() function.

it’s can also work on string with as many words as you want (as long as there is only one space between them).

1 Like

Why using “capture groups” /^(\s+)|\1$/g simply doesn’t work ?

let wsRegex = /(^\s+)(\w+\W+\S+)(\s*$)/;

let result = hello.replace(wsRegex, '$2');

I took the long turn. Could be wrong.

may god bless you
I had everything correct except the “\S” bit which is genius i gotta say and simpler than other solutions in here

1 Like