Running into error creating a Javascript function

Viewed 48

I am trying to write a function to change a string written in Snake Case to Camel Case, but running into an error.

function snakeToCamel(string) {
    arr = [...string];
    for (i of arr) {
        if (i === "_") {
            let upperCaseLetter = arr[i+1].toUpperCase();
            arr.splice(i+1,1,upperCaseLetter);
            arr.splice(i,1)
        }
    };
    return arr;
}

The error is here. I can't find what is wrong in the line stated in the error. What is going on?

snakeToCamel("you_dont_know")
snake-to-camel.js:5 Uncaught TypeError: Cannot read property 'toUpperCase' of undefined
    at snakeToCamel (snake-to-camel.js:5)
    at <anonymous>:1:1
4 Answers

In a for-of loop, the control variable is the array element, not an index. So i in your example is a string. So arr[i+1].toUpperCase(); will do string concatenation and try to look up a property with a name like s1, not 1.

If you want to use the index, you want a for loop or a forEach call (or even map, since that's kind of what you're doing), not a for-of loop.

A couple of other notes:

  • You need to be sure to declare your variables; right now, your code is falling prey to what I call The Horror of Implicit Globals, creating a global called arr. Add const or let before arr.

  • You don't put ; after blocks attached to control-flow statements. (You can have them there, because empty statements are allowed, but they're not supposed to be there.)

For example, using a for loop:

function snakeToCamel(string) {
    // `const` because we never reassign `arr`
    const arr = [...string];
    // Traditional `for` so we have the index
    for (let i = 0, len = arr.length; i < len; ++i) {
        const ch = arr[i];
        if (ch === "_") {
            if (i === len - 1) {
                // Trailing _, just remove it
                arr.splice(i, 1);
                --i;
                --len;
            } else {
                let upperCaseLetter = arr[i + 1].toUpperCase();
                // You can remove the _ and the lowr case letter
                // in a single `splice` rather than multiple ones
                arr.splice(i, 2, upperCaseLetter);
                --i; // Need to do this to allow for multiple `_` in a row
                --len;
            }
        }
    };
    return arr;
}
console.log(snakeToCamel("one_two__three_"));

Or using map:

function snakeToCamel(string) {
    let lastWasUnderscore = false;
    const result = [...string]
        .map(ch => {
            const thisIsUnderscore = ch === "_";
            if (lastWasUnderscore && !thisIsUnderscore) {
                lastWasUnderscore = false;
                return ch.toUpperCase(); // or `.toLocaleUpperCase()`
            }
            lastWasUnderscore = thisIsUnderscore;
            return thisIsUnderscore ? null : ch;
        })
        .filter(ch => ch !== null);
    return result;
}
console.log(snakeToCamel("one_two__three_"));

You were actually fairly close to the solution!

Instead of using a for-of loop I would suggest you use a normal for loop. This way you can access the index i

function snakeToCamel(string) {
    const arr = [...string];
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] === "_") {
            let upperCaseLetter = arr[i+1].toUpperCase();
            arr.splice(i+1,1,upperCaseLetter);
            arr.splice(i,1)
        }
    }
    return arr.join("");
}

Here's the code I wrote. (needs to be refined for DRY principle)

function STC(string) {
    const arr = [...string];
    let output = '';
    for (const letter of arr) {
        if (letter == '_') {
            output += arr[arr.indexOf(letter)+1].toUpperCase()
            arr.splice(arr.indexOf(letter), 1)
        } else {
            output += letter;
        }
    }; return output;
}

I just add to the string instead of an array. I also delete the underscore. Tell me if it works.

function snakeToCamel(string) {
    const arr = [...string];
    arr.forEach((v, i) => {
       if (v === "_") {
            arr.splice(i+1,1, arr[i+1].toUpperCase());
            arr.splice(i,1)
        }
    });
    return arr.join("");
}

const result = snakeToCamel('a_b_c');
console.log(result);

Related