I have an odd problem. I'm trying to use Javascript to fetch me some values from a multidimensional array, and it's giving me some weird output.
Here is my code:
foo = [['3','4'],['5','6']];
for (bar in foo) {
baz = bar[0];
alert(baz);
qux = bar[1];
alert(qux);
}
Here is the output of the above:
// These are all alerts, by the way
0,undefined,1,undefined,$,f,$,c,e,a,c,l,c,l,i,i,n,a,s,l,i,c,o,a,p,g,e,g,e,i,n,c,o,e,r,e,m,f,l,p,i,h,e,r,g
Can somebody tell me what is happening?
Here is a jsFiddle of the problem: http://jsfiddle.net/Jey6w/
Edit:
Here is another jsFiddle, with another layer of "Inception": http://jsfiddle.net/8vyGq/
The output:
// Again, these are all alerts, and * signifies undefined
0**1**$ff$ceaacllcllinnassliicooappgeegeeinncooerremmfllpiiheergg
The JavaScript for ... in loop gives you the names of the object properties, not the values.
Don't use for ... in for real arrays. Use a numeric index or .forEach().
The reason you're getting your output is complicated and uninteresting, since you just shouldn't do that, but here's a start. The property names will be coerced to strings by the for ... in. Thus, on the first iteration, "bar" is the string "0", so ("0")[0] is just "0", and ("0")[1] is undefined because "bar" is a single-character string.
After that, your for ... in loop staggers into some other properties inherited from somewhere; perhaps you're using Prototype or something. The loop then alerts the first two characters of the names of all those other properties.
I could be wrong, but I think it's due to the fact that bar is returning a reference to a property within an object. Changing your selectors to foo[bar][0] works a treat.
foo = [['3','4'],['5','6']];
for (bar in foo) {
alert(foo[bar][0]);
alert(foo[bar][1]);
}
In cases where your object is simply a multi-dimensional array, I would sway array from using the for in statement, as it can select unwanted properties. I would stick to the good old fashioned for(start, stop, increment)
foo = [['3','4'],['5','6']];
for (i = 0; i < foo.length; i++) {
alert(foo[i][0]);
alert(foo[i][1]);
}
Update - jQuery
As there has been mention of jQuery's .each method I thought I'd also post an example of how it could be utilised. The jQuery's each method passes 2 optional parameters, indexInArray, and valueOfElement. Additionally, the jQuery documentation also states that
The value can also be accessed through the this keyword, but
Javascript will always wrap the this value as an Object even if it is
a simple string or number value
With this in mind, we could achieve the same results as previous example, using the following jQuery (jsFiddle):
var foo = [['3','4'],['5','6']];
$.each(foo, function() {
// 'this' is the context of the current item within the array
alert(this[0]);
alert(this[1]);
})
Related
Can someone explain to me the difference of when to use a function by feeding your variables into the parenthesis, and when to tack the function on after the variable with a period, like using the toString() function?
example code
function addMe(a){
a = a+1;
return a;
}
var num = 1;
addMe(num);
num.toString();
I'm not actually sure if my syntax is correct, but I want to know when to feed a variable as a parameter, like how I feed the variable num, to the addMe function. And when to use the function .toString() by putting a period after the variable and typing out the function.
could I have done something like this- provided I built my function correctly?
var num = 1;
num.addMe();
Thanks for the help!
The first is used for simple 'stand alone' functions, while the latter is used for object methods. E.g a number object by default has a toString() method. Some object methods may also require parameters to be passed between the parentheses.
Variables (a function declaration is just a function stored in a variable) are looked up in the scope chain (going up to the next outer scope until a variable with the name is found):
let a = 1; // outer scope
{ // inner scope
console.log(a); // looked up in "inner scope", than "outer scope"
}
Properties of an object are looked up in the objects prototype chain, so if you do
a.b
then a gets looked up in the scopes as explained above, then b is accessed on the resulting object (everything is an object in JavaScript, except for "nothing" (undefined, null)) by looking up the prototype chain. For a simple object, the chain is quite short:
const a = { b: 1 }; // object -> Object.prototype
Here b will be found in the object itself. However all objects inherit from the Object.prototype object, so if you add a property to that (please don't):
Object.prototype.test = 1;
you can then look it up on every object, as the lookup traverses up the prototype chain, and reaches Object.prototype:
console.log({}.test); // 1
Now for numbers (like in your case), they inherit the Number.prototype so you could do:
Number.prototype.addMe = function() {
console.log(this);
};
// two dots are needed to distinguish it from numbers with a fraction (e.g. 1.2)
1..addMe();
That said, now addMe can be called on every number, everywhere in your code. While that might seems useful, it is actually a pain as you don't know where a certain method was added
1..whereDoIComeFrom()
that makes code unreadable and unstructured. Instead if you need a certain functionality multiple times, abstract it into a function, don't touch the native prototypes.
I assume that addMe is just a simplified example, if it isn't, read on:
If you pass an argument to a function in JavaScript, the value will be copied (it is a bit more complicated with non primitives (everything except numbers, booleans etc.)) into the parameter variable of the function called so here:
function addMe(a){
a = a+1;
console.log(a); // 2
return a;
}
var num = 1;
addMe(num);
console.log(num); // 1 ... ?
you actually got two variables (a and num), changing a does not change num. But as you return a you can do:
num = addMe(num);
which copies the value of num into a, then increases a by one and then copues the value of a back to num.
When you did var num = 1 you created a JavaScript object. It looks just like a number but you can think of everything in JavaScript as an object (simplification) and all these objects have different features. So a number has some features, a string has some other features, etc.
You mentioned one feature: toString. Another feature would be toLowerCase.
toString and toLowerCase are functions that come with JavaScript. These functions are then "put on" all of these objects for us to use.
I can have a string variable like
var text = 'MY TEXT'
var lowercaseText = text.toLowerCase()
console.log(lowercaseText) // my text
This code will work because it was decided that the toLowerCase function should work on strings
I can also have an array (list of items)
const list = ['A', 'B', 'C']
const answer = list.toLowerCase()
console.log(answer)
But this code won't work because toLowerCase doesn't work on arrays. So you get the following error message: list.toLowerCase is not a function.
Basically its saying: I don't know what toLowerCase means when used on this list variable (array).
In JavaScript this is called prototypes. Prototype is a way for JavaScript to get some feature from another. Basically: I have all kinds of functions, what object can use what functions. This is called the prototype chain.
In both cases you are using a function. addMe is a function you created and toString is a function in JavaScript that has been placed on objects through this prototype-chain.
Im not actually sure if my syntax is correct
Yes your syntax is correct. Your addMe function is the standard way to create a function in JavaScript.
But i want to know when to feed a variable as a parameter, like how i
feed the variable num, to the addMe function.
Just like you did, you define a function and parameters like you did.
..and when to use the function .toString() by putting a period after
the variable and typing out the function.
When you want to place your function on a object so that all instances of that object can you that object.
In most cases, espcially when you are starting out. You don't have to worry about these prototypes. The way you did.
function addMe(number) {
return number+1
}
const answer = addMe(1) //2
Is a standard way of defining a function and calling it.
I'm trying to learn Javascript - here's my issue:
In the w3schools.com javascript array examples, they show the sequent example:
var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
document.getElementById("demo").innerHTML =
person[0] + " " + person.length;
An array "person" has been defined, but then they proceed to add some elements whit a "named" index. Then tries to print the HTML document the 0th element and the number of elements of the array, like you would do with a standard array.
The description says:
If you use a named index when accessing an array, JavaScript will
redefine the array to a standard object, and some array methods and
properties will produce undefined or incorrect results.
In fact, person[0] and person.length return respectively "undefined" and "0". Even is person was initially defined as an array, by inserting new named indexes elements, the array should be redefined as an object. But when i try do use the Array.isArray() method for checking it, it returns true:
var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
document.getElementById("demo").innerHTML =
person[0] + " " + person.length;
document.getElementById('test').innerHTML = Array.isArray(person);// returns true
So, why? if, as specified by the tutorial, this has been effectively redefined as a standard object, and the ECMAScript 5 has added the .isArray() method for checking if something is an array and nothing else, shouldn't this return false insted of true?
I'm sure i missed something. If i define person like this:
person = {};
then it returns false, as expected. What is happening here? I just wanted to understand arrays a little bit more, this confuses me. Is this just a broken array, but still an array?
Here's the example (without the Array.isarray() bit, just the default): https://www.w3schools.com/js/tryit.asp?filename=tryjs_array_associative_2
First of all I want to note that the example you took from the w3schools page on arrays, is from the "Associative Arrays" section, which has this important introduction:
Many programming languages support arrays with named indexes.
Arrays with named indexes are called associative arrays (or hashes).
JavaScript does not support arrays with named indexes.
In JavaScript, arrays always use numbered indexes.
This puts the example into context, because it really makes no sense to define a variable as an array and then use string keys. But this was an example to illustrate the point.
Does an Array become an Object?
That JavaScript still considers the variable to be an array is as expected. It becomes an array at the moment of assignment of [], and that does not change by adding properties to that object. Yes, arrays are objects. They just have additional capabilities.
The array did not lose any of its array-like capabilities, but those features just don't work on those string properties, ... only on numerical ones (more precisely, the non-negative integer ones).
You loosely quoted the following statement from w3schools:
If you use named indexes, JavaScript will redefine the array to a standard object.
That is wrong information and leads to your misunderstanding. There is no redefinition happening. When you add properties to any object, then the object does not change "type". It remains an instance of what it was before... An array remains an array, a date object remains a date, a regex object remains a regex, even if you assign other properties to it. But non-numerical properties do not "count" for an array: the length will remain unchanged when you add such properties. The length only reveals something about the numerical properties of the object.
This quote is yet another illustration of what the JavaScript community thinks about w3schools.com, i.e. that it is not the most reliable reference, even though it has its value for learning the language.
Example of adding useful properties to arrays
Having said the above, there are cases where you may intentionally want to make use of such properties on arrays. Let's for example think of an array of words that is sorted:
const arr = ["apple", "banana", "grapefruit", "orange", "pear"];
Now let's add something to this array that denotes that it is currently sorted:
arr.isSorted = true;
We could imagine a function that would allow one to add a value to this array, but which also verifies if the array is still sorted:
function addFruit(arr, fruit) {
if (arr.length && fruit < arr[arr.length-1]) {
arr.sorted = false;
}
arr.push(fruit);
}
Then after having added several values, it would maybe be interesting to verify whether the array needs sorting:
if (!arr.sorted) arr.sort();
So this extra property helps to avoid executing an unnecessary sort. But for the rest the array has all the functionality as if it did not have that extra property.
An object that is set up as an array and then filled as an object becomes a member of both classes. Methods of the Array class will apply to its 'array-ness':
Array.isArray(person);
returns true. Methods of the Object class will apply to its 'object-ness':
typeof(person);
returns object. When it could be either one, the 'array-ness' will prevail, because the variable was first defined as an array:
console.log(person);
will put Array [ ] on the console, because it runs the Array class's logging method. It is displayed as an empty array, since it has no numbered elements, but you could add some:
person[2]=66;
and then console.log would log Array [ <2 empty slots>, 66 ].
I think the polyfill implementation of isArray() will clear your doubt by some extent.
#Polyfill
I have come across an interesting piece of code:
function repeat(str,x) {
return Array(x+1).join(str);
}
repeat("wow", 2);
The outcome of this is a string "wowwow". However, I have no idea what this Array(x+1) is actually doing. And very interesting thing is that if I just use Array(x) it prints the str only once and not twice as expected.
When I console.log Array(x+1) it gives this strange output:
Array(x+1) (3) [empty × 3]
I am aware that there exists a repeat() method on strings which can be used happily to achieve the same result as the presented function. But as I've come across it, I would like to know the mechanism behind Array(x+1). I also know what an array or new Array() is. But this I see for the first time.
Array is specified such that new is optional. From the spec:
When called as a constructor it creates and initializes a new Array exotic object. When Array is called as a function rather than as a constructor, it also creates and initializes a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments.
(my emphasis)
To my surprise, the MDN page is not at all clear about this. If I get time, I may have to fix that...
As you can see in console Array(x+1) creates array with 3 empty elements (as far as x = 2). Then you join these elements with string wow so you have:
empty element + "wow" + empty element + "wow" + empty element = "wowwow"
Array(x+1) creates an array of 3 elements all containing empty elements.
Later you are joining these empty elements with the string wow, thus returning:
empty + "wow" + empty + "wow" + empty => wowwow
It is a function that creates an array.
Things like this you should really just try in you console before asking.
These things can be done in Javascript even though they are somewhat contra intuitive:
function A() {
return new Array();
}
let myA1 = A(); // returns an array
let myA2 = new A(); // also returns an array.
EDIT:
The reason is that Javascript is a prototypal language and a "Class" is just a function that we new. If that function is not returning anything we get a new instance of in our case A and if the function returns some other object, that object gets returned.
What is does is to create an array with size x+1(3, in this example). And filling it each cell with the str variable value.
In this code I create an array of size 3 cells and fill them with the string "yolo".
$(document).ready(function(){
var str = "yolo";
console.log(Array(4).join(str));
})
Example fiddle: https://jsfiddle.net/6o90fv9c/
In class, teacher couldn't explain why tweets(i) failed and tweets[i] works:
var tweets=["hi","who","when","where","bye"];
alert("start");
for (var i=0; i < tweets.length; i++) {
alert(tweets[i]);
}
alert("finish");
Brackets are used for functions, so array() would be a function called array. Square brackets are used for arrays, so array[] would be an array. array[0] is the first entry in an array, array(1) would send 1 as an argument to a function called array.
And stop going to classes where the teacher can't explain something this simple. They clearly aren't a programmer.
The reason tweets(i) fails in this code snippet is because, when you say tweets(i), javascript looks at it and says "oh, the code wants me to go find a function named tweets and execute it with a parameter named i."
When javascript sees tweets[i], it says "oh, this isn't a function. The code wants me to find the number-i place in an array and give it back the value stored there.
In short, The reason tweets(i) doesn't work is because you're telling it to alert a function that you haven't defined.
The () is a method invocation operator and the [x] is an member access operator. As array is not a function (e.g. typeof array !== 'function'), so you can only use member access operator on the array.
Note:
I don't know the specification name of the above operators, will need expert explanation on them.
A function is an object so you can use both operators on it
e.g.
var func = function() { return 'hello'; };
func.world = 'earth'
console.log(func());
console.log(func['world'])
console.log(func.world)
The code is the following:
$('input').click(function(){
window.array[$(this).attr('id')]=true;
console.log('Input id: '+$(this).attr('id')+' - Num:'+window.array.length+
' - Array value:'+window.array[$(this).attr('id')]);
alert(window.array.join('\n'));
});
The behavior is very strange: when the event is fired, in the console I can read Input id: example_id - Num:0 - Array value:true, then in the alert I get undefined (empty array? but the console told me the correct value!).
When I click on another input, exactly the same thing happens, such as the previous event hasn't been fired. It seems that the callback function creates an array every time it's called, but I'm using window object, and I was told that attaching arrays to window object is the same of global vars!
Can you explain me this strange behavior?
The length property of an array is its largest numeric property plus one (or zero if it has no numeric properties). So setting non-numeric properties on an array does not change its length.
join only effects numeric properties as well. Still, the result should be an empty string, not undefined. Are you sure you are using a normal array?
Your situation demands that you use an object since you need to use a string as your "id".
if you have defined:
window.array = [ ]
and write:
window.array["id1"] = true; // WRONG!
The above simply means that you'll get:
window.array["id1"] //true but window.array is still [ ]
which is because window.array, though an array is still an object in JavaScript and you can add properties to it but the usage is wrong. To add to an array, you need integers(or integers in the form of string) as indexes.
window.array[1] = true; // OR window.array["1"] = true (!! even this works)