Algorithm: Spinal Tap Case understanding

Algorithm: Spinal Tap Case understanding
0.0 0

#1

Hey everyone

I would like some help understanding the Algorithm challenge “Spinal Tap Case”. Now I did get it to work, but my solution is much longer then what it could be, and what I have seen other people post. Here is my solution:

function spinalCase(str) {
  var regEX1 = /[!@#$%^&*_\s]/gi;
  var regEX2 = /[A-Z]/g;
  str = str.replace(regEX1, "-");
  var arr = str.split('');

  var newArr = [];
  newArr.push(arr[0]);

  for (var i = 1; i < arr.length; i++){
    
    if (arr[i] === "-") {
      newArr.push(arr[i]);
    } else if (arr[i] === arr[i].toUpperCase()) {

      if (arr[i - 1] === "-") {
        newArr.push(arr[i]);
      } else {
        newArr.push("-");
        newArr.push(arr[i]);
      }
      
    } else {
      newArr.push(arr[i]);
    }
  }
      newArr = newArr.join('').toLowerCase();
      return newArr;
    }

    spinalCase("AllThe-small Things");

Anyway so I understand mine, and here a solution I found on the bonfire page.

function spinalCase(str) {
  // Create a variable for the white space and underscores.
  var regex = /\s+|_+/g;

  // Replace low-upper case to low-space-uppercase
  str = str.replace(/([a-z])([A-Z])/g, '$1 $2');

  // Replace space and underscore with -
  return str.replace(regex, '-').toLowerCase();
}

So I can follow this solution however the part that is

$1 $2

I don’t get.

So thats my question, could someone please explain that part of the solution?

Thank You in advance


#2

In the example above the $1 and $2 were used to add a space between any lower case letter followed by an upper case letter. $1 represents ([a-z]) and $2 represents ([A-Z]).

Here’s an example on MDN of how you use $1 and $2:

var re = /(\w+)\s(\w+)/;
var str = 'John Smith';
var newstr = str.replace(re, '$2, $1');
console.log(newstr);  // Smith, John

My solution also included the usage of $1 and $2.

And here's my solution:
function spinalCase(str) {
  var space = /\s/g;
  var capNoSpace = /([a-z])([A-Z])/g;
  var underscore = /[_]/g;
  var strWithHyphens = 
    str.replace(space, "-").replace(capNoSpace, "$1-$2").replace(underscore, "-");
  
  return strWithHyphens.toLowerCase();

}

spinalCase('This Is Spinal Tap');

#3

Thank you for the replay, however I still have a question.

What do $1, $2, refer to? Like how exactly does $1 select “l” in the above example?

I honestly have never seen that be used at all, unless I’m just forgetting I have.


#4

I don’t think I can explain it any better than MDN - Regular Expressions does:

Using parenthesized substring matches:
Including parentheses in a regular expression pattern causes the corresponding submatch to be remembered. For example, /a(b)c/ matches the characters ‘abc’ and remembers ‘b’. To recall these parenthesized substring matches, use the Array elements [1], …, [n].

The number of possible parenthesized substrings is unlimited. The returned array holds all that were found. The following examples illustrate how to use parenthesized substring matches.

The following script uses the replace() method to switch the words in the string. For the replacement text, the script uses the $1 and $2 in the replacement to denote the first and second parenthesized substring matches.


#5

Ah I get it now, it makes so much more since. Thank you for finding that.

I don’t use regular expressions that much currently, so that’s probably why I have never seen that.


#6

Even the example you found on the bonfire page could be simplified. There’s no reason to store anything in a regex variable, for example; all that does is unnecessarily allocate memory. Also, that solution is explicitly checking for underscores and white space to satisfy the test cases, but that doesn’t scale out. What if someone did, this+is+my+string? To me, it makes more sense to replace anything that is not a character (we could include digits too if we wanted).

function spinalCase(str) {
  return str.replace(/[^a-zA-Z]/g, '-').replace(/[a-z](?=[A-Z])/g, '$&-').toLowerCase();
}

#7

Ya that’s why I did one of my regEx like /[!@#$%^&*_\s]/gi to filter out more then just _ or whitespaces.

However I have a question about your code. What does this mean?

(/[a-z](?=[A-Z])/g, '$&-')

I get he [a-z] part, but I do not understand the (?=[A-Z]) part. And I cannot figure out where it would sort out all symbols.

I feel like my regEX1 could be simplified.

Well we did already established that my code is ridiculous anyway :joy:


#8

Hah! It’s not ridiculous. The hardest and most important part is solving the problem, after that it’s icing on the cake.

I did 2 regex look ups, the first one, str.replace(/[^a-zA-Z]/g, '-'), simply says replace any character that’s not lowercase a-z AND not capital A-Z. That’s how it would sort out the symbols. The second regex says, "Match a lower case character preceding a capital character. " The (?=[A-Z]) part is called a positive lookahead; this group matches what comes after the main expression, [a-z] without including it into the result. So in the example of spinalCase it would match l instead of lC.

By no means is my code the only way or necessarily the best way. My regular expression experience is somewhat limited, and I often times spend awhile figuring out how to write them.


#9

Ah now I see thank you for the explanation. What about the '$&-' part?

The rest of it now makes so much more since to me. At least I don’t feel as bad now, I know that getting it to work is the important thing. Getting it to work even more efficiently just comes with experience.

Thanks again.


#10

Hello guys, I got the solution.

In my above code I tried to split the string and join, but even with a fewer lines of code we can get the solution.

All we need to do is replace unwanted part of string with what we need.

spinalCase(“thisIsSpinalTap”) – In this case we can use $operator in regular expression. Two ranges of characters, a-z and A-Z. Characters from these ranges are grouped together. So we need to identify the two characters which are from taken from two respective ranges(For example: “sI”, “sS” and “lT”) .
Then we need to separate these two characters with a space " ". Which we can do by using $operator

str.replace(/([a-z])([A-Z])/g, '$1 $2');

And in the next step, we need to identify the characters which are not alphabets and digits, which we can obtain by using regular expression /W, but it includes _ underscore. So to eliminate underscore we should except it from the regexp. Which we can by using | or Operator.
Then we need to replace these characters with - hyphen.

str.replace(/\W|_/g, "-");

Finally we convert the string to lower case using .toLowerCase() method.

My Solution is:

function spinalCase(str) {
var newArr= str.replace(/([a-z])([A-Z])/g, ‘$1 $2’).replace(/\W|_/g, “-”);
str = newArr.toLowerCase();
return str;
}
spinalCase(‘This Is Spinal Tap’);