Javascript: TypeError variable is undefined - javascript

I am currently building a small web application with similar functionality across all modules. I want to code small generic functions so that all programmers next to me, call these functions and these functions return necessary but important data for them to implement their functionality. In this example, I am trying to deal with the typical "choose true or false" exercise. So from the template.php they call this function:
function checkAnswers(){
var radiobuttons = document.form1.exer1;
var correctAnswers = answers(); //this is an array of string
var checkedAnswers = checkExerciseRB(radiobuttons, 2, correctAnswers);
for(i=0; i<checkedAnswers.length; i++){
alert(checkedAnswers[i]);
}
}
Function checkExerciseRB is my generic function, it is called from checkAnswers.
function checkExerciseRB(rbuttons, opciones, correct){
var answers = new Array();
var control = 0;
for(i=0; i<rbuttons.length; i++){
var noPick="true";
for(j=0; j<opciones; j++){
if(rbuttons[control+j].checked){
if(rbuttons[control+j].value==correct[i]){
answers[i]= 1;
noPick="false";
break;
}
else{
answers[i]=2;
noPick="false";
break;
}
}
}
if(noPick=="true")
answers[i]=0;
control=control+opciones;
}
return answers;
}
It works great but while looking at my favorite browsers (FireFox, Chrome) error log it says:
TypeError: rbuttons[control + j] is undefined
Any clue on how to deal with this matter?

This probably means that control + j is greater than or equal to the length of the array rbuttons. There's no such array element as rbuttons[control + j].
You should learn how to use the JavaScript debugger in your favorite browsers! Debuggers are great. They let you watch this code run, line by line, as fast or as slow as you want, and watch how the value of control changes as you go.
You’ll watch it, and you’ll think “Oh! That line of code is wrong!”

You're looping through rbuttons.length times, but in each loop you're adding 2 to control. Using control to index your array, you're going to run past the end.

Does the index specified by control + j exist in the array? i.e: If that evaluates to 4, is there at least 5 items in the array?
Also, you should be using var i, var j, etc inside your for loop. Without it your variables are leaking into the scope this code is executed in (most likely the global scope, and that's not good) :)

Related

calling a function in for loop giving infinite loop but same works fine in while loop

So i was working on a function which takes anything as argument and console logs it inside a box or a cell. This is what i came up with:
var horiz='━';
var vert='┃';
var ulcor='┏';
var urcor='┓';
var blcor='┗';
var brcor='┛';
//create a cell/box
var cell=(d='')=>{
let h='';
if(d==undefined||d==null){
throw('cell needs an argument');
}
len=d.toString().length;
for(i=0;i<3;i++){
if(i==0){
for(j=0;j<len;j++){
h+=horiz;
}
if(i==0) console.log(ulcor+h+urcor);
}
if(i==1){
console.log(`${vert}${d}${vert}`);
}
if(i==2){
console.log(blcor+h+brcor);
}
}
}
Now when i was testing this i did following
cell('index');
cell(1);
gave following output:
┏━━━━━┓
┃index┃
┗━━━━━┛
┏━┓
┃1┃
┗━┛
and did other testing as well and it worked pretty well until i did the following test:
for(i=0;i<5;i++){
cell(i)
}
Above code should give me cells/boxes with 1,2,3,4 but instead this gave me an infinite loop and what i noticed is that its only printing cells with 4 (not 1,2 or 3) but infinite times.
And now here is the fun part. When I do following:
n=1;
while(n<10){
cell(n);
n+=1;
}
above code gives desired result and works fine.
Weird!! just Couldn't figure out why its running infinite times when i used it in the for loop but while loop works good. If any sort of improvement required in the code then please do mention it:)
When you declare a variable without the var, let, or const keywords, that variable is an implicit global. In your cell function, you use for(i=0;i<3;i++) where i is declared without a keyword.
Your calling for loop for(i=0;i<5;i++){ cell(i) } also uses i without a keyword which makes i refer to the same global variable. So your call function is actually manipulating i from within the function which makes the for loop behave in the unintended manner. The while loop uses a different variable, n, which is why it doesn't have the same problem.
Try changing the for loop inside call to for(let i = 0; i < 3; i++) to limit the scope of i. And, in general, use let and const to declare ALL variables, even (and especially) the ones in for loops.

javascript use console.log without moving to new line

I am new to js and hope this is not too trivial, but I am unable to find any help on the net.
I wish to output to console.log and prevent moving to a new line, so the next time the output will be appended to the same line. ie,
"use strict";
for (let i = 0; i<=9;i++){
console.log(i); // here i would like to freeze the output so the result is 0123456789 on one line, rather than those digits in a column.
}
I have seen fixes involving assigning the outputs to a string and printing in 1 hit, but that seems incredibly crude. Even in Fortran 4 as I recall in the '70s, you could prevent moving to a new line before printing again, so I think I am missing something fundamental. Also I cannot find any general help on formatting numerical output in javascript. Can someone point me in the right direction?
Thanks
Unfortunately, the console.log() method will only write out a string to a single line and doesn't support the appending behavior you are looking for.
As you detailed in your original post, you could accomplish writing the final result out through the use of a variable (i.e. displaying the final concatenated string), but not continually appending to the same line within the console itself as the loop is being iterated over.
Alternative Grouping Option
The concept of grouping entries is supported, which is obviously very different than your original ask, but it may be worth considering as mentioned in the documentation for console.group() and might look something like this:
var rollingConcatenation = '';
console.group("Looping Group Example");
for (let i = 0; i<=9;i++){
rollingConcatenation += i;
console.log(rollingConcatenation);
}
console.groupEnd();
This can give your console the following appearance, which can help with readability (depending on your use cases):
Do It Yourself Implementation
Another option might be to store your current console value within a variable and at clear it and rewrite the updated values out. Depending on your very specific use cases, you could achieve the behavior you are looking for using something like this crude implementation:
// Define a custom console
var customConsole = {
// Store a reference to your backing value
tempValue: '',
// Always write out the most recent value
log: function(msg) {
this.tempValue += msg;
console.clear();
console.log(this.tempValue);
},
// A clear method to clear the backing console
clear: function() {
this.tempValue = '';
console.clear();
}
}
for (var i = 0; i < 10; i++) {
// Use your custom console instead of the normal one
customConsole.log(i);
}
Take a new variable outside the loop and then prepare that string inside the loop and then you can console.log() outside the loop.
var str = '';
for (let i = 0; i <= 9; i++) {
str += i;
}
console.log(str);

JavaScript For Loop Breaking Early

Why would a for loop terminate early in JavaScript? For some reason my outer for loop terminates after the first repetition. I am new to JavaScript but would expect something like this to work in Java.
function check(){
var elements = document.getElementById('fields').children;
var filteredMolecules = molecules;
console.log(elements.length);
for (i = 0; i < elements.length; i++) {
console.log(elements[i].id)
filterMolecules(filteredMolecules, elements[i].id, 0, 10);
}
}
function filterMolecules(molecules, parameter, lower, upper){
console.log('filtering');
var filteredMolecules = [];
for (i=0;i<molecules.length;i++){
var value = molecules[i].val()[parameter];
filteredMolecules.push(molecules[i]);
}
molecules=filteredMolecules;
}
In check(), I loop through elements which contains 22 items as shown by the first console.log(elements.length). If I remove the method filterMolecules(...) then all 22 IDs are logged. However, with the code as is, only the first id is logged.
I believe the filterMolecules method which should run elements.length number of times is causing the outer for loop to not work. Could someone please explain why this is happening. If relevant, in filterMolecules(...) the data is retrieved from Google Firebase with molecules[i].val()[parameter]. Additionally, both methods use the global variable molecules (line 3 and line 14)
When you don't declare variables in javascript you end up using globals (which can be a difficult to spot source of bugs). So here you are using the same global variable i for both loops. When you start looping thought molecules you are accidentally incrementing the counter loop of your first for. Use different variables or define them with :
for (let i=0;i<molecules.length;i++)
Which will give each loop its own version of i.
In this case, since the declarations are inside individual functions, you could use var too:
for (var i=0;i<molecules.length;i++) {
// etc.
}

Restricted JavaScript Array Pop Polyfill not working

I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)

Run the same function on each item in a list / array

Goal
I have a working function (JSFiddle). On numerous occasions throughout a script the function runs sequentially. In these instances, there is a lot of repetitious code that I would like to consolidate.
Ideally changing code like this:
functionName("First_item") +
functionName("Second_item") +
functionName("Third_item") +
To something like this:
functionName("First_item", "Second_item", "Third_item");
The function will run for each item in the list so the result is the same but the code more elegant and maintainable.
Notes:
I’m not looking to use any libraries (e.g. jQuery) to accomplish the goal.
Solution
Amit Joki’s answer kindly noted I could use arguments. When I implemented the code, the modified function (JSFiddle) only returned the output string for the first argument / item.
Vanice’s answer pointed out the eventual solution.
Make one string from the output of all arguments / items by concatenating (joining) the output strings within the for loop (with the use of +=).
Return the concatenated output by placing the return outside of the for loop.
Example
Working solution (JSFiddle).
Thanks
Thank you very much to everyone for their time and help on this. I really appreciate it!
Leveraging Javascript's Prototype OOP: You can add an each function to Array's themselves, so every array in your code that will automatically have an inhereted each function.
Array.prototype.each = function(callback){
for (var i = 0; i < this.length; i++){
callback(this[i]);
}
}
Usage:
myArray.each(myCoolFunction)
['something','somethingelse',somethingother'].each(myCoolFunction)
myArray.each( function (item) {
// if your item has a method
item.Something();
// if you'd like to call a function on the item:
doSomething(item);
});
caveats:
Because javascript is an asynchronous language that is interpreted differently across various browsers and inherently handles primitive objects and complex objects differently, it is highly recommended usage of underscore or lodash. You can also make your own, but you'll need ensure the objects passed through will be handled by the functions properly. This may include workarounds or creating special callback functions for different object types that are passed through your each function.
For more information: Is JavaScript a pass-by-reference or pass-by-value language?
Libraries you should seriously consider:
lodash:
https://lodash.com/docs#forEach
_([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
// → logs each number and returns '1,2,3'
_.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
// → logs each number and returns the object (property order is not guaranteed across environments)
underscore:
http://underscorejs.org/#each
_.each([1, 2, 3], alert);
=> alerts each number in turn...
You don't need an array. Just use arguments
function functionName(){
for(var i = 0; i < arguments.length; i++){
// do something with arguments[i];
}
}
and then you can do
functionName("shot_type","shot_height","shot_angle","framed","scene_depth");
P.S #codebox's solution works if supporting legacy IE version isn't a problem. Don't know why he deleted it...so putting it here so it helps. His answer using forEach
["shot_type","shot_height","shot_angle","framed","scene_depth"].forEach(FunctionName);
EDIT: Looking at your Fiddle, you have a return inside the for loop - therefore the function will return after the first iteration. Put the return after the for and concatenate the output to one string.
var output = "";
for(...){
output += description_of_object + ": " + randomly_selected_item_from_object + ".\n";
}
// return it
return output;
With Javascript only:
var actions = ["shot_type","shot_height","shot_angle","framed","scene_depth"];
for (var i = 0; i < actions.length; i++){
FunctionName(actions[i]);
}
With JQuery:
$.each(["shot_type","shot_height","shot_angle","framed","scene_depth"], function(index,value){
FunctionName(value);
});
I haven't tested it but it should work.
To avoide redundancy in code use an array with the values, that you want to pass through the function and call the function in an loop.
var vals=["shot_type","shot_height","shot_angle","framed","scene_depth"];
for(var i=0; i<vals.length; i++)
{
FunctionName(vals[i]);
}
If you want to expand the function (adding another parameter) you can just expand the for-loop and the array structure.
Alternatively you could fill an object with the values and handle this logic in an object. But this would just do a difference on calling the function.

Categories

Resources