JavaScript: Repeated code vs. extra functions when code length is same - javascript

After updating my code based on answers to this previous question, I came up with the following solution:
var Coder = (function() {
var controlWords = [
['ONE','OO'],
['TWO','TT'],
['THREE','RR'],
//['...','..'],
['END','NN']
],
map = {
'0':' ', '1':'_', '2':',',
'3':'.', '4':'?', '5':'!',
'6':'\'','7':'"', '8':'(',
'9':')', 'a':'o', 'b':'d',
'c':'a', 'd':'e', 'e':'p',
'f':'i', 'g':'f', 'h':'v',
'i':'u', 'j':'l', 'k':'m',
'l':'y', 'm':'q', 'n':'x',
'o':'b', 'p':'j', 'q':'t',
'r':'n', 's':'z', 't':'w',
'u':'k', 'v':'h', 'w':'s',
'x':'c', 'y':'r', 'z':'g'
},
reverseMap = (function() {
var j, tmp = {};
for (j in map){
if (Object.prototype.hasOwnProperty.call(map, j))
tmp[map[j]] = j;
}
return tmp;
})(),
value, i,
encode = function(data) {
var input = (typeof data == 'string' ? data : '');
console.log('Input to encode: '+input);
for (i = 0; i < controlWords.length; i++) {
value = new RegExp(controlWords[i][0],'g');
input = input.replace(value,controlWords[i][1]);
}
console.log('Encode step 1: '+input);
input = input.replace(/./g, function(c){
return reverseMap[c]
|| reverseMap[c.toLowerCase()].toUpperCase();
});
console.log('Encoding output: '+input);
return {length: input.length, data: input};
},
decode = function(data) {
var input = (typeof data == 'string' ? data : '');
console.log('Input to decode: '+input);
input = input.replace(/./g, function(c){
return map[c]
|| map[c.toLowerCase()].toUpperCase();
});
console.log('Decode step 1: '+input);
for (i = 0; i < controlWords.length; i++) {
value = new RegExp(controlWords[i][1],'g');
input = input.replace(value,controlWords[i][0]);
}
console.log('Decoding output: '+input);
return {length: input.length, data: input};
};
return {encode: encode, decode: decode};
})();
var str = 'ONE Hello, TWO JavaScript THREE World! END',
enc = Coder.encode(str).data,
dec = Coder.decode(enc).data;
As you can see, there's a lot of fully- or nearly-repeated code. The only meaningful differences are the order in which the two transformations happen, whether map or reverseMap is used, and which index of each control word array is used as the regex and which as the replacement value.
To abide by the concept of wrapping repeated code in a sub-function and calling that function, I made the following attempt. It defines the two transformations as internal functions, and then based on the value of the type argument, decides the rest.
var Coder = (function() {
var controlWords = [ /* same */ ],
map = { /* same */ },
reverseMap = /* same */,
code = function(data, type) {
var input = (typeof data == 'string' ? data : ''),
mapping, x, y, value, i,
transform = function() {
return input.replace(/./g, function(c){
return mapping[c]
|| mapping[c.toLowerCase()].toUpperCase();
});
},
replace = function() {
for (i = 0; i < controlWords.length; i++) {
value = new RegExp(controlWords[i][x],'g');
input = input.replace(value,controlWords[i][y]);
}
return input;
};
if (type == 'decode') {
mapping = map;
x = 1;
y = 0;
input = transform();
input = replace();
} else if (type == 'encode') {
mapping = reverseMap;
x = 0;
y = 1;
input = replace();
input = transform();
} else {
throw new Error('Invalid type argument!');
}
return {data: input, length: input.length};
};
return {code: code};
})();
var str = 'ONE Hello, TWO JavaScript THREE World! END',
enc = Coder.code(str, 'encode').data,
dec = Coder.code(enc, 'decode').data;
However, you might notice that this code is actually longer. It's still more easily extended, if I wanted to add more types than 'encode' and 'decode' (not going to). But currently less efficient?
I then went back to the version with two functions (to avoid the passing of and check of 'type'):
var Coder = (function() {
var controlWords = [ /* same */ ],
map = { /* same */ },
reverseMap = { /* same */ },
input, mapping, x, y, value, i,
transform = function() {
return input.replace(/./g, function(c){
return mapping[c]
|| mapping[c.toLowerCase()].toUpperCase();
});
},
replace = function() {
for (i = 0; i < controlWords.length; i++) {
value = new RegExp(controlWords[i][x],'g');
input = input.replace(value,controlWords[i][y]);
}
return input;
},
encode = function(data) {
input = (typeof data == 'string' ? data : '');
mapping = reverseMap;
x = 0;
y = 1;
input = replace();
input = transform();
return {length: input.length, data: input};
},
decode = function(data) {
input = (typeof data == 'string' ? data : '');
mapping = map;
x = 1;
y = 0;
input = transform();
input = replace();
return {length: input.length, data: input};
};
return {encode: encode, decode: decode};
})();
// Call methods same as first example
So lengthy post to basically ask, which is better, repeated code or extra functions/checks, when it doesn't really help code length and there's no plans to extend the code or release it as a public project? Which of these is the most elegant solution, or is there an even better?
EDIT
One way I thought of to clean it up in general is to remove the variable declarations of mapping, x, and y and just pass them as arguments to replace and transform. This would yield
decode = function(data) {
var input = (typeof data == 'string' ? data : '');
input = transform(map);
input = replace(1,0);
return {length: input.length, data: input};
}

Related

Javascript issue with precedence

I have built this function to replace a group of characters in a string by a random value from another list within a function:
function replaceExpr(a) {
var expToReplace = 0
var newSent = a
while (expToReplace == 0) {
if (a.search("zx") == -1) {
expToReplace = 1
} else {
var startPos = a.search("zx");
startPos += 2;
var endPos = a.search("xz");
var b = a.substring(startPos, endPos);
var fn = window[b];
if (typeof fn === "function") var newWord = fn();
final = newSent.replace("zx" + b + "xz", newWord);
newSent = final
a = a.replace("zx" + b + "xz", "")
}
}
return final
}
function appearance() {
var list = [
"attractive",
"fit",
"handsome",
"plain",
"short",
"tall",
"skinny",
"well-built",
"unkempt",
"unattractive"
]
return list[Math.floor(Math.random() * list.length)];
}
function personality() {
var list = [
"aggresive",
"absent-minded",
"cautious",
"detached from the real world",
"easygoing",
"focused",
"honest",
"dishonest",
"polite",
"uncivilized"
]
return list[Math.floor(Math.random() * list.length)];
}
An example :
var a = replaceExpr("Theodor is a zxappearancexz man. He seems rather zxpersonalityxz.")
alert(a)
// Theodor is a unattractive man. He seems rather cautious.
Everything works perfectly with the function but I have an issue related to it. As you can see, there's one grammar mistake : it's written "a unattractive" where it should be "an unattractive".
There's a function I usually use to to fix the a\an issue which is :
var AvsAnSimple = (function (root) {
//by Eamon Nerbonne (from http://home.nerbonne.org/A-vs-An), Apache 2.0 license
// finds if a word needs a "a" or "an" before it
var dict = "2h.#2.a;i;&1.N;*4.a;e;i;o;/9.a;e;h1.o.i;l1./;n1.o.o;r1.e.s1./;01.8;12.1a;01.0;12.8;9;2.31.7;4.5.6.7.8.9.8a;0a.0;1;2;3;4;5;6;7;8;9;11; .22; .–.31; .42; .–.55; .,.h.k.m.62; .k.72; .–.82; .,.92; .–.8;<2.m1.d;o;=1.=1.E;#;A6;A1;A1.S;i1;r1;o.m1;a1;r1; .n1;d1;a1;l1;u1;c1.i1.a1.n;s1;t1;u1;r1;i1;a1;s.t1;h1;l1;e1;t1;e1.s;B2.h2.a1.i1;r1;a.á;o1.r1.d1. ;C3.a1.i1.s1.s.h4.a2.i1.s1;e.o1.i;l1.á;r1.o1.í;u2.i;r1.r1.a;o1.n1.g1.j;D7.a1.o1.q;i2.n1.a1.s;o1.t;u1.a1.l1.c;á1. ;ò;ù;ư;E7;U1;R.b1;o1;l1;i.m1;p1;e1;z.n1;a1;m.s1;p5.a1.c;e;h;o;r;u1.l1;o.w1;i.F11. ;,;.;/;0;1;2;3;4;5;6;71.0.8;9;Ae;B.C.D.F.I2.L.R.K.L.M.N.P.Q.R.S.T.B;C1;M.D;E2.C;I;F1;r.H;I3.A1;T.R1. ;U;J;L3.C;N;P;M;O1. ;P1;..R2.A1. ;S;S;T1;S.U2.,;.;X;Y1;V.c;f1.o.h;σ;G7.e1.r1.n1.e;h1.a3.e;i;o;i1.a1.n1.g;o2.f1. ;t1.t1. ;r1.i1.a;w1.a1.r1.r;ú;Hs. ;&;,;.2;A.I.1;2;3;5;7;B1;P.C;D;F;G;H1;I.I6;C.G.N.P.S1.D;T.K1.9;L;M1;..N;O2. ;V;P;R1;T.S1.F.T;V;e2.i1.r;r1.r1.n;o2.n6;d.e1.s;g.k.o2;l.r1;i1.f;v.u1.r;I3;I2;*.I.n1;d1;e1;p1;e1;n1;d2;e1;n1;c1;i.ê.s1;l1;a1;n1;d1;s.J1.i1.a1.o;Ly. ;,;.;1;2;3;4;8;A3. ;P;X;B;C;D;E2. ;D;F1;T.G;H1.D.I1.R;L;M;N;P;R;S1;m.T;U1. ;V1;C.W1.T;Z;^;a1.o1.i1.g;o1.c1.h1.a1;b.p;u1.s1.h1;o.ộ;M15. ;&;,;.1;A1;.1;S./;1;2;3;4;5;6;7;8;Ai;B.C.D.F.G.J.L.M.N.P.R.S.T.V.W.X.Y.Z.B1;S1;T.C;D;E3.P1;S.W;n;F;G;H;I4. ;5;6;T1;M.K;L;M;N;O1.U;P;Q;R;S;T1;R.U2. ;V;V;X;b1.u1.m;f;h;o2.D1.e.U1;..p1.3;s1.c;Ny. ;+;.1.E.4;7;8;:;A3.A1;F.I;S1.L;B;C;D;E3.A;H;S1. ;F1;U.G;H;I7.C.D1. ;K.L.N.O.S.K;L;M1;M.N2.R;T;P1.O1.V1./1.B;R2;J.T.S1;W.T1;L1.D.U1.S;V;W2.A;O1.H;X;Y3.C1.L;P;U;a1.s1.a1.n;t1.h;v;²;×;O5;N1;E.l1;v.n2;c1.e.e1.i;o1;p.u1;i.P1.h2.i1.a;o2.b2;i.o.i;Q1.i1.n1.g1.x;Rz. ;&;,;.1;J./;1;4;6;A3. ;.;F1;T.B1;R.C;D;E3. ;S1.P;U;F;G;H1.S;I2.A;C1. ;J;K;L1;P.M5;1.2.3.5.6.N;O2.H;T2;A.O.P;Q;R1;F.S4;,...?.T.T;U4;B.M.N.S.V;X;c;f1;M1...h2.A;B;ò;S11. ;&;,;.4.E;M;O;T1..3.B;D;M;1;3;4;5;6;8;9;A3. ;8;S2;E.I.B;C3.A1. ;R2.A.U.T;D;E6. ;5;C3;A.O.R.I1.F.O;U;F3;&.H.O1.S.G1;D.H3.2;3;L;I2. ;S1.O.K2.I.Y.L3;A2. ;.;I1. ;O.M3;A1. ;I.U1.R.N5.A.C3.A.B.C.E.F.O.O5. ;A1.I;E;S1;U.V;P7;A7;A.C.D.M.N.R.S.E1. ;I4;C.D.N.R.L1;O.O.U.Y.Q1. ;R;S1;W.T9.A1. ;C;D;F;I;L;M;S;V;U7.B.L.M.N.P.R.S.V;W1.R;X1.M;h1.i1.g1.a1.o;p1.i1.o1;n.t2.B;i1.c1.i;T4.a2.i2.g1.a.s1.c;v1.e1.s;e1.a1.m1.p;u1.i2.l;r;à;Um..1.N1..1.C;/1.1;11. .21.1;L1.T;M1.N;N4.C1.L;D2. .P.K;R1. .a;b2;a.i.d;g1.l;i1.g.l2;i.y.m;no. ;a1.n.b;c;d;e1;s.f;g;h;i2.d;n;j;k;l;m;n;o;p;q;r;s;t;u;v;w;p;r3;a.e.u1.k;s3. ;h;t1;r.t4.h;n;r;t;x;z;í;W2.P1.:4.A1.F;I2.B;N1.H.O1.V;R1.F1.C2.N.U.i1.k1.i1.E1.l1.i;X7;a.e.h.i.o.u.y.Y3.e1.t1.h;p;s;[5.A;E;I;a;e;_2._1.i;e;`3.a;e;i;a7; .m1;a1;r1. .n1;d2; .ě.p1;r1;t.r1;t1;í.u1;s1;s1;i1. .v1;u1;t.d3.a1.s1. ;e2.m1. ;r1. ;i2.c1.h1. ;e1.s1.e2.m;r;e8;c1;o1;n1;o1;m1;i1;a.e1;w.l1;i1;t1;e1;i.m1;p1;e1;z.n1;t1;e1;n1;d.s2;a1. .t4;a1; .e1; .i1;m1;a1;r.r1;u1.t.u1.p1. ;w.f3. ;M;y1.i;h9. ;,;.;C;a1.u1.t1;b.e2.i1.r1;a.r1.m1.a1.n;o4.m2.a1; .m;n8; .b.d.e3; .d.y.g.i.k.v.r1.s1. ;u1.r;r1. ;t1;t1;p1;:.i6;b1;n.e1;r.n2;f2;l1;u1;ê.o1;a.s1;t1;a1;l1;a.r1; .s1; .u.k1.u1. ;l3.c1.d;s1. ;v1.a;ma. ;,;R;b1.a.e1.i1.n;f;p;t1.a.u1.l1.t1.i1.c1.a1.m1.p1.i;×;n6. ;V;W;d1; .t;×;o8;c2;h1;o.u1;p.d1;d1;y.f1; .g1;g1;i.no. ;';,;/;a;b;c1.o;d;e2.i;r;f;g;i;l;m;n;o;r;s;t;u;w;y;z;–;r1;i1;g1;e.t1;r1.s;u1;i.r3. ;&;f;s9.,;?;R;f2.e.o.i1.c1.h;l1. ;p2.3;i1. ;r1.g;v3.a.e.i.t2.A;S;uc; ...b2.e;l;f.k2.a;i;m1;a1. .n3;a3; .n5.a;c;n;s;t;r1;y.e2; .i.i8.c2.o1.r1.p;u1.m;d1;i1.o;g1.n;l1.l;m1;o.n;s1.s;v1.o1;c.r5;a.e.i.l.o.s3. ;h;u1.r2;e.p3;a.e.i.t2.m;t;v.w1.a;xb. ;';,;.;8;b;k;l;m1;a.t;y1. ;y1.l;{1.a;|1.a;£1.8;À;Á;Ä;Å;Æ;É;Ò;Ó;Ö;Ü;à;á;æ;è;é1;t3.a;o;u;í;ö;ü1; .Ā;ā;ī;İ;Ō;ō;œ;Ω;α;ε;ω;ϵ;е;–2.e;i;ℓ;";
function fill(node) {
var kidCount = parseInt(dict, 36) || 0,
offset = kidCount && kidCount.toString(36).length;
node.article = dict[offset] == "." ? "a" : "an";
dict = dict.substr(1 + offset);
for (var i = 0; i < kidCount; i++) {
var kid = node[dict[0]] = {}
dict = dict.substr(1);
fill(kid);
}
}
fill(root);
return {
raw: root,
//Usage example: AvsAnSimple.query("example")
//example returns: "an"
query: function (word) {
var node = root, sI = 0, result, c;
do {
c = word[sI++];
} while ('"‘’“”$\''.indexOf(c) >= 0);//also terminates on end-of-string "undefined".
while (1) {
result = node.article || result;
node = node[c];
if (!node) return result;
c = word[sI++] || " ";
}
}
};
})({})
Now, the problem is that I can't find a way to use this function in conjunction with the replaceExpr. The following obviously wouldn't work because of order precedence :
var a = replaceExpr("Theodor is " + AvsAnSimple(zxappearancexz) + "man. He seems rather " + AvsAnSimple(zxpersonalityxz).")
I just recently started learning javascript so my knowledge is rather limited. Any ideas how I could overcome this?
Thank you!
You could use a regular expression to optionally match the " a " or "an" before your word in the input string and store that matched portion in a variable using the String.match() function, then check if that " a " or " an " exists in your matched string, do the manipulations you need to do and store that manipulated string in a separate variable, then use String.replace() to find that previously matched string again, and replace it wit
your manipulated string. The regular expression you could use for this is /(\san?\s)?(zx\w*zx)/gm
See the regular expression here for more context.
Thank you Joseph! With your help I managed to find something that works by using your regular expression. Here's my function :
function replaceExpr(a) {
var nbExprToReplace = 1;
while (nbExprToReplace == 1) {
if (a.search("zx") == -1) {
nbExprToReplace = 0;
} else {
var currentGroup = a.match(/(\san?\s)?(zx\w*xz)/);
var exprToChange = currentGroup[2];
exprToChange = exprToChange.slice(2,-2);
var exprToChange = window[exprToChange];
if (typeof exprToChange !== "function") {
alert("the keyword is not a recognized function!");
break;
} else {
exprToChange = exprToChange();
var final = exprToChange
};
if (currentGroup[1] === undefined) {
} else {
var newArticle = AvsAnSimple.query(exprToChange);
final = newArticle.concat(" " + final)
};
a = a.replace(currentGroup[0], " " + final);
};
};
return a;
};

Variable substitution within a string

I'm trying to come up with some very reusable code that will look up and perform variable substitutions within a string.
The example string below contains a $$ reference to a variable. Format is varname.key.
I want the subText() function to be reusable. The issue I'm having is repvars themselves can require substitution. The code hasn't finished substituting the example text and I'm asking it to substitute the repvars.cr by calling the same function. This seems to through it off. I'm saying that because if I do it separately in works.
var exampleText = "A string of unlimited length with various variable substitutions included $$repvars.cr$$";
var repvars = {
cr: 'Copyright for this is $$repvars.year$$',
year: '2019'
}
function subText(text) {
var subVars = findSubs(text);
return makeSubs(text, subVars);
}
function findSubs(theText) {
var subarr = [];
while (theText.indexOf('$$') > -1) {
theText = theText.substring(theText.indexOf('$$') + 2);
subarr.push(theText.substring(0, theText.indexOf('$$')));
theText = theText.substring(theText.indexOf('$$') + 2);
}
return subarr;
}
function makeSubs(text, subs) {
for (var s = 0; s < subs.length; s++) {
var subst = getSubVal(subs[s]);
text = text.split("$$" + subs[s] + "$$").join(subst);
}
return text;
}
function getSubVal(subvar) {
var subspl = subvar.split('.');
switch (subspl[0]) {
default:
return processRepVar(subspl[1]);
}
}
function processRepVar(rvName) {
var data = getRepVarData(rvName);
if(data.indexOf('$$') > -1) {
subText(data);
} else {
return data;
}
}
function getRepVars() {
return repvars;
}
function getRepVarData(key) {
return getRepVars()[key];
}
subText(exampleText);
Aren't you just missing a return here?
function processRepVar(rvName) {
var data = getRepVarData(rvName);
if(data.indexOf('$$') > -1) {
subText(data);
} else {
return data;
}
}
Changing subText(data) to return subText(data); makes your code work for me.
Working jsfiddle: https://jsfiddle.net/uzxno754/
Have you tried regular expressions for this?
function replace(str, data) {
let re = /\$\$(\w+)\$\$/g;
while (re.test(str))
str = str.replace(re, (_, w) => data[w]);
return str;
}
//
var exampleText = "A string with variables $$cr$$";
var repvars = {
cr: 'Copyright for this is $$year$$',
year: '2019'
}
console.log(replace(exampleText, repvars))
Basically, this repeatedly replaces $$...$$ things in a string until there are no more.

Extend Javascript Syntax to Add Typing

I'd like to extend javascript to add custom type checking.
e.g.
function test(welcome:string, num:integer:non-zero) {
console.log(welcome + num)
}
which would compile into:
function test(welcome, num) {
if(Object.prototype.toString.call(welcome) !== "[object String]") {
throw new Error('welcome must be a string')
}
if (!Number.isInteger(num)) {
throw new Error('num must be an integer')
}
console.log(welcome + num)
}
What's the most straightforward way of doing this?
So far i've looked at:
sweet.js (online documentation looks out of date as I think it's going through some sort of internal rewrite)
esprima and escodegen (not sure where to start)
manually parsing using regular expressons
After evaluating all the various options, using sweet.js appears to be the best solution. It's still fairly difficult to get working (and I am probably doing stuff the wrong way) but just in case someone want's to do something similar this here was my solution.
'use strict'
syntax function = function(ctx) {
let funcName = ctx.next().value;
let funcParams = ctx.next().value;
let funcBody = ctx.next().value;
//produce the normal params array
var normalParams = produceNormalParams(funcParams)
//produce the checks
var paramChecks = produceParamChecks(funcParams)
//produce the original funcBody code
//put them together as the final result
var params = ctx.contextify(funcParams)
var paramsArray = []
for (let stx of params) {
paramsArray.push(stx)
}
var inner = #``
var innerStuff = ctx.contextify(funcBody)
for (let item of innerStuff) {
inner = inner.concat(#`${item}`)
}
var result = #`function ${funcName} ${normalParams} {
${paramChecks}
${inner}
}`
return result
function extractParamsAndParamChecks(paramsToken) {
var paramsContext = ctx.contextify(paramsToken)
//extracts the actual parameters
var paramsArray = []
var i = 0;
var firstItembyComma = true
for (let paramItem of paramsContext) {
if (firstItembyComma) {
paramsArray.push({
param: paramItem,
checks: []
})
firstItembyComma = false
}
if (paramItem.value.token.value === ',') {
firstItembyComma = true
i++
} else {
paramsArray[i].checks.push(paramItem.value.token.value)
}
}
for (var i = 0; i < paramsArray.length; i++) {
var checks = paramsArray[i].checks.join('').split(':')
checks.splice(0, 1)
paramsArray[i].checks = checks
}
return paramsArray
}
function produceNormalParams(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
//Produces the final params #string
var inner = #``
var first = true
for (let item of paramsArray) {
if (first === true) {
inner = inner.concat(#`${item.param}`)
} else {
inner = inner.concat(#`,${item.param}`)
}
}
return #`(${inner})`
}
function produceParamChecks(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
var result = #``
for (let paramObject of paramsArray) {
var tests = produceChecks(paramObject)
result = result.concat(#`${tests}`)
}
return result
}
function produceChecks(paramObject) {
var paramToken = paramObject.param
var itemType = paramObject.checks[0]
var checks = paramObject.checks
if (itemType === undefined) return #``
if (itemType === 'array') {
return #`if (Object.prototype.toString.call(${paramToken}) !== "[object Array]") throw new Error('Must be array:' + ${paramToken})`
else {
throw new Error('item type not recognised: ' + itemType)
}
}
}

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();
};
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
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]++;
}
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;
}
}

JavaScript: Having trouble returning custom object information

I've made some new objects with object methods and I'm having trouble returning the information.
I intend for allPages to be a 2d array:
var allPages = [[]];
function textbox(type)
{
this.type=type;
this.getInfo = function () { return ( this.type ); };
}
function addTextbox(dropdown)
{
var myindex = dropdown.selectedIndex;
var SelValue = dropdown.options[myindex].value;
if(SelValue == "String")
{
var tb = new textbox("string");
allPages[allPages.length-1].push(tb);
var string = "";
for (i = 0;i < allPages.length;i++)
{
for(j = 0;j < allPages[i].length;j++)
{
string = string + allPages[i][j].getInfo;
}
}
<!-- Problem here: prints "function () { return this.type; }"-->
document.write(string);
}
}
}
You are not calling the function, you are referencing it
allPages[i][j].getInfo;
should be
allPages[i][j].getInfo();
3 lines above where you state the problems exists, it should be:
string = string + allPages[i][j].getInfo(); // mind the () at the end.

Categories