xflave
xflave

Reputation: 143

charAt is not a function

I'm trying to create a key mapping that keeps track of the frequency for each character of a string in my createArrayMap() function but I keep getting this error from firebug: TypeError: str.charAt(...) is not a function

I found the charAt() function on Mozilla's developer website it should be a function that exists.

var input;
var container;
var str;
var arrMapKey = [];
var arrMapValue = [];


function initDocElements() {

    container = document.getElementById("container");
    input = document.getElementById("inputbox");

}

function createArrayMap() {
    str = input.value;
    for (var i = 0; i < str.length; i++) {
        if (arrMapKey.find(str.charAt(i)) == undefined) {
            arrMapKey.push(str.charAt(i));
            arrMapValue.push(1);
        }
    }
}

function keyPressHandler() {
    createArrayMap();
    console.log(arrMapKey);
    console.log(arrMapValue);
}

function prepareEventHandlers() {
    input.onfocus = function() {
        if (this.value == "Start typing here!") {
            this.value = "";
        }
    };
    input.onblur = function() {
        if (this.value == "") {
            this.value = "Start typing here!";
        }
    };
    input.onkeyup = keyPressHandler;
}

window.onload = function() {
    initDocElements();
    prepareEventHandlers();
};

Upvotes: 11

Views: 60751

Answers (3)

Noctis
Noctis

Reputation: 11763

This has been answered, but here's my version of your problem JSBIN LINK (also has an object option in addition to the array solution).

I moved some variables around so you'll have less global ones, added comments, and mocked with the output so it'll show it on the page instead of the console.

besides the Array.find() issues, you weren't initializing your arrays on the build method, and so, you would have probably ended with the wrong count of letters.

HTML:

<div id="container">
<textArea id="inputbox"></textArea></div>
<p id="output">output will show here</p>

JS:

var input,      // Global variables
    container,  //
    output;     //

/**
 * Initialize components
 */
function initDocElements() {
    container = document.getElementById("container");
    input = document.getElementById("inputbox");
    output = document.getElementById("output");
}

/**
 * Creates the letters frequency arrays.
 * Note that every time you click a letter, this is done from scratch.
 * Good side: no need to deal with "backspace"
 * Bad side: efficiency. Didn't try this with huge texts, but you get the point ...
 */
function createArrayMap() {
    var index,                  // obvious
        tempChar,               // temp vars for: char 
        tempStr = input.value,  // string
        len = tempStr.length,   // for loop iteration
        arrMapKey   = [],       // our keys
        arrMapValue = [];       // our values

    for (var i = 0 ; i <len ; i++) {

        // These 2 change each iteration
        tempChar = tempStr.charAt(i);
        index = arrMapKey.indexOf(tempChar); 

        // If key exists, increment value
        if ( index > -1) {          
            arrMapValue[index]++;
        }   
        // Otherwise, push to keys array, and push 1 to value array
        else {                      
            arrMapKey.push(tempChar);
            arrMapValue.push(1);
        }
    }

    // Some temp output added, instead of cluttering the console, to the 
    // a paragraph beneath the text area.
    output.innerHTML = "array keys:   "+arrMapKey.toString() + 
        "<br/>array values:"+arrMapValue.toString();
}

function keyPressHandler() {
    createArrayMap();

}

function prepareEventHandlers() {
    input.onfocus = function() {
        if (this.value == "Start typing here!") {
            this.value = "";
        }
    };
    input.onblur = function() {
        if (this.value === "") {
            this.value = "Start typing here!";
        }
    };
    input.onkeyup = keyPressHandler;
}

window.onload = function() {
    initDocElements();
    prepareEventHandlers();
};

BTW, as the comments suggest, doing this with an object will is much nicer and shorter, since all you care is if the object has the current char as a property:

/**
 * Same as above method, using an object, instead of 2 arrays
 */
function createObject() {
    var index,                  // obvious
        tempChar,               // temp vars for: char 
        tempStr = input.value,  // string
        len = tempStr.length,   // for loop iteration
        freqObj = {};           // our frequency object

    for (var i = 0 ; i <len ; i++) {
        tempChar = tempStr.charAt(i);   // temp char value

        if (freqObj.hasOwnProperty(tempChar))
            freqObj[tempChar]++;
        else
            freqObj[tempChar] = 1;
    }
}

Upvotes: 0

TMan
TMan

Reputation: 1905

You're not going about things in the most efficient manner... What if you changed it to look like this so you are continually updated with each keypress?

var keyMap = {};
...
input.onkeyup = keyPressHandler;

function keyPressHandler(e) {
  var char = String.fromCharCode(e.keyCode);
  if(!(char in keyMap))
    keyMap[char] = 1;
  else
    keyMap[char]++;
}

Upvotes: 0

Olaf Dietsche
Olaf Dietsche

Reputation: 74048

The problem is not with String.charAt(), but with Array.find().

The first argument to find is a callback, but the result of str.charAt(i) is a character and not a callback function.

To search for an element in your array, you could use Array.indexOf() as @adeneo already suggested in a comment

function createArrayMap() {
    var str = input.value;
    for (var i = 0; i < str.length; i++) {
        if (arrMapKey.indexOf(str.charAt(i)) == -1) {
            arrMapKey.push(str.charAt(i));
            arrMapValue.push(1);
        }
    }
}

See JSFiddle

Upvotes: 5

Related Questions