DEV Community

Discussion on: Solving "Spinal Tap Case" / freeCodeCamp Algorithm Challenges

Collapse
 
willsmart profile image
willsmart • Edited

Nice one!
This can be made a little cleaner though by using zero-width assertions to divide up the camelCased words, that way you don't need to reinsert any captures and can do it all in one go...
I'd do something like:

kebabMe = s => s.replace(/(?<=[A-Za-z])(?=[A-Z])|[^A-Za-z]+/g, '-').toLowerCase()

the /(?<=[A-Za-z])(?=[A-Z])/ part matches the gap between words, i.e. between a letter and a following upper-case letter but not the letters themselves.

So, the whole regex matches any one of those gaps, and any string of non-letters.
Of course, this screws up numbers completely 🤷‍♂️

Collapse
 
virenb profile image
Viren B

thanks for the thorough explanation on that! 🤯🤯

Collapse
 
willsmart profile image
willsmart

No worries, hope it helps!

BTW I've updated the main line of code from /(?<=[a-z])(?=[A-Z])... to /(?<=[A-Za-z])(?=[A-Z])... matching the explanation below (sort of changed my mind on that halfway through writing).

Using [A-Za-z] just makes sure that SentencesContainingAWordWithOneLetter get translated correctly.
Of course it breaks support for CONSTANT_CASE, but what can you do?

Thread Thread
 
ttatsf profile image
tatsuo fukuchi • Edited

How do you like this?

const spinalCase = s => 
  s.replace(
    /(?<=[Aa-z])(?=[A-Z][a-z])|(?<=[a-z])(?=A)|[^A-Za-z]+/g
    , '-'
  )
  .toLowerCase()

spinalCase('SentencesContainingAWordWithOneLetter CONSTANT_CASE')  
// 'sentences-containing-a-word-with-one-letter-constant-case'
Thread Thread
 
willsmart profile image
willsmart

Well, it's a trade-off. On the one hand "AAAA" is the word "A" four times in UpperCamelCase, on the other hand it's the word "AAAA" in CONSTANT_CASE. In the end the problem is just ambiguous.

You've solved the tradeoff by only breaking camelcase words where the next word has more than one char
That may be a fair strategy since it means that constant case is well covered, but does mean some pretty reasonable camel case sentences won't work. ("ThisIsASpinalTap" -> "this-isa-spinal-tap" )

In the end, it's about figuring out which cases are important and what rules you want to cover.

I think a good medium might be to take blocks of uppercase letters surrounded by non-letters, and stop them from being clobbered as camel case words by lowercasing them.

f = s => s
  .replace(
    /(?<=[^A-Za-z]|^)[A-Z]+(?=[^A-Za-z]|$)/g, 
    w=>w.toLowerCase()
  )
  .replace( // edited to handle numbers a bit
    /(?<=[A-Za-z])(?=[A-Z0-9])|(?<=[0-9])(?=[A-Za-z])|[^A-Za-z0-9]+/g,
    '-'
  ).toLowerCase()


a=[
"ThisIsASpinalTapAAAA",
"THIS_IS_A_SPINAL_TAP_A_A_A_A",
"thisIsASpinalTapAAAA",
"this-is-a-spinal-tap-a-a-a-a",
"this is a spinal tap a a a a"
]
a.map(f) 
// -> [
"this-is-a-spinal-tap-a-a-a-a", 
"this-is-a-spinal-tap-a-a-a-a",
 "this-is-a-spinal-tap-a-a-a-a", 
"this-is-a-spinal-tap-a-a-a-a", 
"this-is-a-spinal-tap-a-a-a-a"
]