2

I am experiencing some weird behaviour that I think is scope related, but I can't explain why it's happening.

So this is the problematic piece of code. I'm trying to Camel Case some words and differentiate between text separated by dashes and underscores. Ignore the fact that the algorithm isn't completely correct yet, that's not the point. This is the stage I noticed the problem.

function toCamelCase(str){
    let isDash = false
    for (let char in str) {
      if (char === "-") {
        isDash = true
      }
    }
    if (isDash) {
      return str.split("-").map(word => word[0].toUpperCase() + word.slice(1, word.length + 1)).join("")
    } else {
      return str.split("_").map(word => word[0].toUpperCase() + word.slice(1, word.length + 1)).join("")
    }
  }

let result = "why-false"
console.log(toCamelCase(result))

So isDash determines which type of separator does the text contain. I can obviously use an arrow function instead of this approach, and it would work perfectly. Something like this:

let isDash = () => {
for (let char in str) {
  if (char === "-") {
    return true
  }

But I can't understand why doesn't the previous example work as well. I've read about scope and it should be possible to reassign to that variable if it was defined at a higher level. Even if I defined isDash outside the function, it still didn't work. It's value always remains the initial one: false. Why isn't the if statement changing its value to true?

let x = [1, 4, 5, 7]
let txt = 0
let working = "no"

for (let i in x) {
  txt += x[i]
  if (txt > 4) {
    working = "yes"
  }
}

console.log(working)

As you can see, if I write something similar to demonstrate if it's possible to use the variable in a nested if statement, it works fine and working will log yes. What am I missing here? I would really appreciate some help, this really is a dead end to me and I wouldn't have asked for help if I could've figured it out myself. Big thanks to anyone that can explain this stuff to me!

qz-
  • 674
  • 1
  • 4
  • 14
Armand Felix
  • 43
  • 1
  • 6
  • FWIW, in the future, the first thing to do is to check your assumptions: have you logged or used a debugger to actually check what's in `char`? This should always be the first step. – Dave Newton Oct 24 '21 at 17:00
  • Thank you for the advice! I'm pretty much a noob right now so I'm not sure how to use the debugger, but I'll watch some tutorials today and learn everything about it. – Armand Felix Oct 24 '21 at 18:23

3 Answers3

3

The issue here is your for loop:

    for (let char in str) {
      if (char === "-") {
        isDash = true
      }
    }

The for-in loop loops over indices or keys, not values (you can test this by doing console.log(char); in the loop). Instead, use a for-of loop.

qz-
  • 674
  • 1
  • 4
  • 14
  • Thank you for your answer! But why does the for-in loop work when iterating over an array? As you see in my last example, it worked without issues. From what I understand, a string is also a type of array, so my confusion arised from the fact that it worked on the array and not on the string.. – Armand Felix Oct 24 '21 at 18:25
1

The issue is that you're using the for...in statement, which loops over enumerable property names of an object.

for (let char in str) {
      if (char === "-") {
        isDash = true
      }
    }

You can use for...of statement to iterate over Strings and use const if you are not going to reassign the variable inside the block.

This way:

for (const char of str) {
  if (char === "-") {
    isDash = true
  }
}
  • Thank you so much! It works the way it should now. I didn't know about the for-of loop until now and the fact that the for-in loop for the array in the last example and not for the string (a type of array) confused me. – Armand Felix Oct 24 '21 at 18:28
  • It's just that the values iterated on are different. for...of returns a list with the values while for...in will result in a list of keys. For example: let string = 'String' for (const char in string) { console.log(char); // => "0", "1", "2","3", "4", "5" } for (const char of string) { console.log(char); // => "S","t","r","i","n","g" } – Sabri Oussama Oct 24 '21 at 18:57
  • Ok, that cleared a lot for me. Appreciate your detailed explanation. I now have fully realized what my mistake was! – Armand Felix Oct 24 '21 at 23:36
-3

I'm fairly certain the reason for this is your use of 'let' instead of 'var'. (To be honest I hadn't seen it used before this post...).

See this article: https://www.google.com/amp/s/www.geeksforgeeks.org/difference-between-var-and-let-in-javascript/amp/

And if this fixes your issue, have a read up further on the difference s between the two.

I personally would always just use var .

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Dean O'Brien
  • 335
  • 2
  • 11
  • 1
    It isn't, and it's recommended to use `let`/`const` instead of `var` under almost all circumstances (this has been the case for some time now). – Dave Newton Oct 24 '21 at 16:58
  • Generally I'd say try to post an answer only when you're confident in the answer, otherwise this could go in a comment. Scoping was a misdiagnosis in the initial question. – qz- Oct 24 '21 at 17:04
  • I stand corrected – Dean O'Brien Oct 24 '21 at 17:04