How does jQuery.each() work with associative arrays (objects)? - javascript

I have an associative array with two object inside. Running this through $(myassoc).each(), the callback runs only once. Also the callback parameters (index and object) returns 0 and the entire associative array, respectively.
One would expect jQuery.each() to run for each element in the array, returning the correct keys as index and the correct element as the object.
Why isn't that happening, and can jQuery do what I'm after?

I think you're looking for jQuery.each() instead of .each()
try this:
$.each(myassoc, function(index, value){
//your code
});

try this:
$.each(assocarray,function(i, value){
console.log('index: ' + i + ',value: ' + value);
});

Badly.
Don't $(associative_array).each(function () {...}) -- that's nonsense
Don't $.each(associative_array, function() {...}); -- that has an obscure bug(1)
To see the bug, try this in a javascript console:
> $.each({foo:1, length:-1, bar:2}, console.log)
foo 1
length -1
bar 2
> $.each({foo:1, length:0, bar:2}, console.log)
The first example outputs three lines of key-value pairs, as it should. The second outputs nothing!
The moral of the story, don't use jQuery.each() on objects. (Objects in JavaScript are essentially the same thing as associative arrays.) Things may work fine forever, but you run the risk that someday an object happens to have a member named length and its value happens to be exactly 0 and then you have a bug out of nowhere that can be very difficult to explain. (I'll let you guess, by the ponderous heft of this answer, whether that ever happened to me.)
As mentioned in the bug report:
If you need to iterate over all keys of objects having the length property, jQuery.each is not the correct solution.
I suggest going further, that jQuery.each should not be relied upon for associative arrays, ever.
(1) This "bug" may never be fixed, since $.each() historically uses Duck Typing on arrays: "Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index."
Here's what I use[thanks Dominik] to loop through property names and values of objects, or put another way, the keys and values of an associative array:
function looper(object, callback) {
for (var key in object) {
if (object.hasOwnProperty(key)) {
if (false === callback.call(object[key], key, object[key])) {
break;
}
}
}
return object;
}
looper() is then a drop-in replacement for $.each()
> looper({foo:1, length:0, bar:2}, console.log)
foo 1
length 0
bar 2
Just like $.each():
Inside the callback, this is each value
Inside the callback, returning false (not just falsy) terminates the loop
looper() returns the object originally passed to it
looper() works on arrays as well as objects.
Use:
var a = [];
looper({foo:1, length:0, bar:2}, function(k, v) {
a.push(k+"="+v);
});
console.assert("foo=1,length=0,bar=2" === a.join());
Try that with $.each() and you'll get an empty result. Because it interprets this particular object as an array-like object of zero length.

The problem is that the $.each() function internally retrieves and uses the length property of the passed collection. But in an associative array that has no integer indices the length always seems to be 0. For $.each() now there seems to be nothing to walk through.
The $.each() function internally retrieves and uses the length
property of the passed collection.
The solutions is simply to use an object instead.
var obj = {
"flammable": "inflammable",
"duh": "no duh"
};
$.each( obj, function( key, value ) {
alert( key + ": " + value );
});

Related

javascript Object.each is not a function

While iterating over an object i get the error,
Object.each is not a function,
Here's my code:
$("#print").click(function() {
$values = {};
$("[data-type=text]").each(function(i,e){
if($(e).val() !='') {
$values[$(e).attr('name')] = $(e).val();
}
});
console.log($values);
$values.each(function(i,e){
console.log(i.e);
});
});
moreover, i can't use for loop too, since i don't know the keys .
In your code, $values is not a jQuery Object and .each() will not work.
Iterate over a jQuery object, executing a function for each matched element.
You will want to use $.each() instead:
$.each($values, function(k,v){
console.log(k + ": " + v);
});
A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties.
You can also just send an Object (jQuery or not) to console:
console.log($values);

Javascript array type casting

I've found something interesting and I don't know why it's happening.
If I try in google chrome developer tools, the following two staments
(Array([1,2,3])).filter(function (item, index, array) {
return item === 1;
}); ==> []
and
([1,2,3]).filter(function (item, index, array) {
return item === 1;
}); ==> [1]
The results are an empty array for the first statement and array with a single value (1) for the second
Inspecting the parameters for the callback function, i found that in the first statement the arguments are (array, index, value) and for the second statemente are(value, index, array).
Inspecting with typeof and constructor of both objects the result are the expected, and the same "object", and Array.
Why is this happening?
Thanks
Because that's not how you define an array with Array().
Should be without the square brackets, otherwise it's an array of a single element, which is also an array ([1,2,3]).
Array(1,2,3)
That inner array never equals to 1 (basically you check [1,2,3] == 1), so the result is an empty array.
If you define an array by using Array([1,2,3]) this code, then the following array will be created,
[[1,2,3]]
Since you are pushing an array into another one. And if you really want the Array function to create an array by reading an array then you have to write something like this,
Array.apply([], [1,2,3])
But the above one is completely pointless. Again I am telling it is completely pointless since we are having an array that we require in our hand. But just for a knowledge you can know about it.
Array([1,2,3]) create array of arrays [[1, 2, 3]] so .map()function will iterate one time only.
If you want to create array with Array constructor use next syntax:
Array(1,2,3)
the shorter is the better :
[1,2,3].filter(item => item === 1);

get element from arbitrary list in javascript

i have a function in javascript where i need to retrieve the last element of the list. the list can be an array, string, list in numbers (not array). I tried converting the list into a String and then an array and retrieving it by index, but that's not working.
Here is the code I tried:
function last(list){
var array = new String(list);
array = array.split("");
return array[array.length-1];
}
I don't understand what the problem is because test suite says Expected: 5 instead got: 5
I am using code wars and did not write the tests. Is it expecting a Number and getting a String '5' ? I don't understand types in loosely typed languages very well yet.
from the comments, I think you mean you want to either return the last element in an array, the last character in a string, or the last argument passed if multiple arguments were passed. This would do it:
function last() {
if (arguments.length > 1) { // first we handle the case of multiple arguments
return Array.prototype.pop.call(arguments);
}
value = arguments[0]
if (typeof value === 'string') { // next, let's handle strings
return value.split('').pop();
}
if (Object.prototype.toString.call( [] ) === '[object Array]') {// Arrays are of type object in js, so we need to do a weird check
return value.pop();
}
}
arguments is a pseudo-array that contains all arguments passed into the function, so for last(1,2,3,4,5), arguments would be roughly [1,2,3,4,5]. It's not exactly that though, because arguments has all args in order and a length property, but it's prototype isn't Array, so it isn't truly [1,2,3,4,5] and lacks all of array's functions. This is why we need to call pop in the context of arguments (in javascript, Function.prototype.call calls the function passing the first arguments as the value of this, and all the rest of the arguments into the arguments pseudo-array, so for example last.call([], 1, 2, 3) would call last in the context of a new array and with arguments roughly equal to [1,2,3]).
the rest of the code is pretty straightforward, except for the check to see if value is an array, which is further explained here.
Finally, pop is an array method that removes the last element from an array and returns it.

for ( key in array) loops over array prototype

I don't know how to word this problem exactly but I found this extremely wired.
Basically I did this test in chrome's developer tool console.
for (var request in [0,1,2]) { console.log(request);}
0
1
2
compare
the last four lines are all outputs from the for loop.
during the for loop, request got the value compare.
I wonder if this is a bug in chrome.
for ... in ... iterates over the enumerable properties of an object, and is not intended for array indices. Array indices are also enumerable properties, but as you've discovered anything unsafely added to Array.prototype will be returned too.
To safely add a (non-enumerable) method to Array.prototype in ES5 browsers you can use Object.defineProperty, e.g.:
Object.defineProperty(Array.prototype, 'compare', {
value: function() {
...
}
});
This will stop for ... in from breaking, but it's still the wrong tool for the job when the variable of interest is an array.
You're best off using an indexed for loop.
For..in also enumerates over inherited properties etc.
var request = [0,1,2];
for (var i = 0; i < request.length; i++) {
console.log(request[i]);
}
The top answer to this question:
stackoverflow previous answer
puts it better than I could:
in your case, the global "object-prototype" as a compare function declared for it, e.g...
object.prototype.compare = function (a,b) {return a === b}
...and so, whenever you iterate an object (an array being one kind of object) you also iterate over the "compare" function of it's prototype... which is a "member" of it.
As others pointed out for .. in is not the best way to iterate thru array. If you insist on using it for some reason - use hasOwnProperty method to determine that property indeed belongs to the array:
var arr = [0,1,2];
for (var request in arr ) {
if (arr.hasOwnProperty(request)) console.log(request);
}

recursively concatenating a javascript functions arguments

I came across a javascript puzzle asking:
Write a one-line piece of JavaScript code that concatenates all strings passed into a function:
function concatenate(/*any number of strings*/) {
var string = /*your one line here*/
return string;
}
# meebo
Seeing that the function arguments are represented as an indexed object MAYBE an array, i thought can be done in a recursive way. However my recursive implementation is throwing an error. --"conc.arguments.shift is not a function" --
function conc(){
if (conc.arguments.length === 0)
return "";
else
return conc.arguments.shift() + conc(conc.arguments);
}
it seems as though conc.arguments is not an array, but can be accessed by a number index and has a length property??? confusing -- please share opinions and other recursive implementations.
Thanks
arguments is said to be an Array-like object. As you already saw you may access its elements by index, but you don't have all the Array methods at your disposal. Other examples of Array-like objects are HTML collections returned by getElementsByTagName() or getElementsByClassName(). jQuery, if you've ever used it, is also an Array-like object. After querying some DOM objects, inspect the resulting jQuery object with Firebug in the DOM tab and you'll see what I mean.
Here's my solution for the Meebo problem:
function conc(){
if (arguments.length === 0)
return "";
else
return Array.prototype.slice.call(arguments).join(" ");
}
alert(conc("a", "b", "c"));
Array.prototype.slice.call(arguments) is a nice trick to transform our arguments into a veritable Array object. In Firefox Array.slice.call(arguments) would suffice, but it won't work in IE6 (at least), so the former version is what is usually used. Also, this trick doesn't work for collection returned by DOM API methods in IE6 (at least); it will throw an Error. By the way, instead of call one could use apply.
A little explanation about Array-like objects. In JavaScript you may use pretty much anything to name the members of an object, and numbers are not an exception. So you may construct an object that looks like this, which is perfectly valid JavaScript:
var Foo = {
bar : function() {
alert('I am bar');
},
0 : function() {
alert('I am 1');
},
length : 1
}
The above object is an Array-like object for two reasons:
It has members which names are numbers, so they're like Array indexes
It has a length property, without which you cannot transform the object into a veritable Array with the construct: Array.prototype.slice.call(Foo);
The arguments object of a Function object is pretty much like the Foo object, only that it has its special purpose.
Mozilla on the subject:
The arguments object is not an array. It
is similar to an array, but does not have any array
properties except length. For example,
it does not have the pop method.
However it can be converted to an real
array:
var args = Array.prototype.slice.call(arguments);
Therefore the solution to your problem is fairly simple:
var string = Array.prototype.slice.call(arguments).join("");
BTW: It further states:
The arguments object is a local
variable available within all
functions; arguments as a property of
Function can no longer be used.
You should only use arguments instead of func.arguments
This works:
function concatenate(){
return [].join.call(arguments, "");
}
alert(concatenate("one", "two", "three"));
The arguments list is not a genuine array. I think you can borrow the array methods and use them on arguments with "call" or "apply."
You could do this:
function concatenate() {
if (arguments.length > 1) {
return arguments[0] + concatenate.apply(this, Array.prototype.splice.call(arguments, 1));
}
return arguments[0];
}
It works with at least 6 different methods of Array.prototype (I suspect there are more), namely join, slice, splice, concat, reduce, flat. Probably the shortest way is using join, which is #svinto's solution above. g is identical to #GeorgSchölly's answer.
Voilà the six solutions:
function f(){
return [].join.call(arguments,'');
}
function g(){
return [].slice.call(arguments).join('');
}
function h(){
return [].splice.call(arguments,0).join('');
}
function i(){
return [].concat.apply([], arguments).join('');
}
function j(){
return [].reduce.call(arguments,function(buff,x){return buff+x},'');
}
function k(){
return [].flat.call(arguments).join('');
}
document.getElementById('F').textContent = f('a','b','c');
document.getElementById('G').textContent = g('a','b','c');
document.getElementById('H').textContent = h('a','b','c');
document.getElementById('I').textContent = i('a','b','c');
document.getElementById('J').textContent = j('a','b','c');
document.getElementById('K').textContent = k('a','b','c');
<pre id='F'></pre>
<pre id='G'></pre>
<pre id='H'></pre>
<pre id='I'></pre>
<pre id='J'></pre>
<pre id='K'></pre>

Categories

Resources