Improper array handling by jQuery: length= 0 bug? - javascript

I don't know if this is my little knowledge of jQuery or it is just a bug, but here's what happens. I have this small piece of JSON code
{
"planes":[
{
"id":1,
"name":"Boeing 767-300",
"height":54.9 ,
"wingspan":47.6,
"vel": 851,
"vel max":913,
"plane width":283.3,
"weight":86070,
"full weight":158760,
"passengers":{
"1 class":350,
"2 class":269,
"3 class":218
},
"fuel tank":90.625,
"engine":"2 turbofan General Electric CF6-80C2"
},
{
"id":2,
"name":"Boeing 737-800",
"height":33.4 ,
"wingspan":35.8,
"vel": 840,
"vel max":945,
"plane width":105.44,
"weight":32704,
"full weight":56472,
"passengers":{
"1 class":189
},
"fuel tank":90.625,
"engine":"2 turbofan CFM56-3C1"
}
]
}
which I'm then getting with jQuery's getJSON without any flaw. Then I want two separate arrays: one holding the keys and the other holding the values, and again no problem with Object.keys and Object.values. By logging the result in a single string, everything is fine. Until I try to construct an associative array using keys as indexes and values as data. By logging the result, I get an extra "length" index with value "0". here's my jQuery code
var arr=[];
$.getJSON("js/jsondata.json", function(data){
var keys= Object.keys(data.planes[0]);
var values= Object.values(data.planes[0]);
//im only testing on the first object, for now
$.each(keys, function(i){
//creating the associative index and assigning the value
arr[keys[i]]= values[i];
console.log("Key: "+ keys[i]+", Value: "+values[i]);
//this logs the exact values and indexes
});
console.log(arr);
//this logs an extra "length" 0
});

What you really want to use is a key-value object and not an array. So you have at least to options:
Actually the arrays are objects, and you will be able to attach/add new properties, however, this kind of objects have a pre-defined prototype and properties. One of these properties is length. Cause that, you're getting an "unexpected" property length.
Changing this var arr = []; to this var arr = {};.
Changing this var arr = []; to this var arr = Object.create(null);.
Adding properties to an object array
let arr = [2];
arr['myKey'] = 'EleFromStack';
console.log(arr.myKey);
console.log(arr.length); // 1 cause length is part of Array type.
Adding properties to a key-value object
let arr = {}; // Object.create(null);
arr['myKey'] = 'EleFromStack';
console.log(arr.myKey);
console.log(arr.length); // undefined cause length is not part of the Object type.

Biggest problem is there's no such beast as associative arrays in JavaScript. All arrays must have numbered indices. Association the way you want is handled with objects.
So, you can just assign the first plane in your planes array to a variable and retain your original association rather than iterate it.
Is there a particular reason you're trying to disassemble and reassemble your object to an array this way?

Related

How can I force a string of an integer to behave like a string when using it to deindex an associative array in node?

I have the following code:
var arr = [];
var int_str = "9";
arr[int_str] = true;
console.log(arr);
arr[int_str + ""] = true;
console.log(arr);
arr[int_str.toString()] = true;
console.log(arr);
Giving the output:
[ <9 empty items>, true ]
[ <9 empty items>, true ]
[ <9 empty items>, true ]
In other words, when the string int_str is used as the key to the array arr, it behaves like the number 10, rather than the string "10", and 9 empty cells initialize behind the 11th. This happens despite using toString(), or trying to force it in to becoming a string with + "".
The only way I can force this to behave like a string is to append an actual character like int_str + ".". That's not a practical solution for me though.
Use a Map instead of an array [] and simply avoid that nonsense...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
Maps take strings, numbers, objects, and symbols and do not modify their key types.
If you want to be able to pass a string as a true "key", then you can't use an Array as Arrays only take non-numeric whole numbers as their indexes.
If you pass a string as an indexer to an array, an attempt to convert that string to a non-negative whole number is made and, if successful, that numeric index is located within the array. If not, and you are setting a value, you won't be adding a new item to the array. You'll be creating a new property on the Array instance and that value won't show up when you enumerate the array.
Examples:
let myArr = [1,2,3,4,5,6,7,8,9];
console.log("Arrays:");
console.log(myArr[5]); // 6
console.log(myArr["5"]); // 6
console.log(myArr["ten"]); // undefined - Arrays don't take keys
// Objects:
let myObj = {
one:1,
two:2,
three:3,
4:4,
five:5
};
console.log("Objects:");
console.log(myObj["4"]); // 4
console.log(myObj[4]); // 4 - The number is treated as a string because keys are strings
console.log(myObj["five"]); // 5
console.log(myObj[2]); // undefined - objects don't take indexes and there is no key of 2

Setting the value of a multidimensional object or array property in Javascript

Yes I know how to loop through arrays (types) in Javascript. The fact is, I'd like to know how to set a multiDimensionalArray array's value by a set of given indexes to keep it as generic as possible. For example I've an array with a length of 3 (which could as well be a length of 4, 100, ...):
var indexes = [0, "title", "value"];
I know the multidimensional array (mArray) can be set by putting the indexes like so:
multiDimensionalArray[0]["title"]["value"] = "Jeroen"; or multiDimensionalArray[indexes[0]][indexes[1]][indexes[2]] = "Jeroen";
The fact that the given indexes array can vary and does not always contain the same index names so I'm search for a solution like this:
multiDimensionalArray[indexes] = "Jeroen";
I don't know how to code the assignation if this. I've searched on Google/Stack Overflow. Maybe I'm using the wrong keywords. Can anyone help me?
Thanks!
Following example is how I've made it working thanks to Jonas's example:
var json = [{
"hello": {
"world": 1,
"world2": 2
},
"bye": {
"world": 1,
"world2": 2
}
}];
var indexes = [0, "hello", "world2"];
var value = "value";
indexes.slice(0,-1).reduce((obj, index) => obj[index], json)[indexes.pop()] = value;
console.log(json);
So imagine you have a structure like this:
var array=[[["before"]]];
Then you want
var indexes=[0,0,0];
var value="value";
to actually do:
array[0][0][0]="value";
which can be easily achieved with reduce:
indexes.slice(0,-1).reduce((obj,index)=>obj[index],array)[indexes.pop()]=value;
Explanation:
indexes.slice(0,-1) //take all except the last keys and
.reduce((obj,index)=>obj[index] //reduce them to the resulting inner object e.g. [0,0] => ["before"]
,array) //start the reduction with our main array
[indexes.pop()]=value;// set the reduced array key to the value
var array=[[[0]]];
var indexes=[0,0,0];
var value="value";
indexes.slice(0,-1).reduce((obj,index)=>obj[index],array)[indexes.pop()]=value;
console.log(array);

How to remove all undefined keys from a javascript array (hopefully when creating the array?)

I have placed my frustrations into a jsfiddle to observe here: http://jsfiddle.net/8ShFr/1/
var brand_new_array = new Array();
brand_new_array[10] = "random array value";
alert('why does this array have a length of ' + brand_new_array.length + '???');
I am doing some calculations client side that require me to set javascript array keys of 1M+ in number.
Not knowing exactly what that number is demands that I iterate through the first 1M+ empty array values before getting to an array key that holds data.
I simply want to set a single large key value for a javascript array without creating a bunch of empty keys before it?
I am using jQuery.each to iterate over the array, and it keeps going through array[0], array[1], array[2], etc... when I only set array[123125] for example.
Just filter out the undefineds.
brand_new_array = brand_new_array.filter(function(n){return n !== undefined});
The reason for the length being 10 is that an array's length is set to the largest index number in the array. However, this does not mean there are 9 other values in there because in javascript an array is at its base an object.
The length is just a property in the object. Arrays in javascript are at their core objects (Array Object 1). They merely act like arrays through an api.
"Whenever a property is added whose name is an array index, the length property is changed, if necessary, to be one more than the numeric value of that array index" 1
1. ECMAScript Language Specification 15.4 Array Objects
You probably want to just use an object with strings for keys (the keys can be the toString() of Numbers, which will happen automatically if you try to use numbers).
var sparse_array_obj = {};
sparse_array_obj[10003210234] = 4; // Fair dice roll
sparse_array_obj[5] = 17; // Truly random number
sparse_array_obj[900] = Math.random(); // Pseudorandom number
for(var i in sparse_array_obj)
console.log(sparse_array_obj[i]);
The downside is that Javascript provides no guarantees about the iteration order through an object (since its keys are unordered by definition). There are however ways around this, such as:
// Sort the keys in numeric order
var sorted_keys = Object.keys(sparse_array_obj).sort(function(a, b){ return a - b; });
for(var i = 0; i < sorted_keys.length; i++)
console.log(sparse_array_obj[sorted_keys[i]]);
Object.keys needs to be shimmed in older browsers.
var brand_new_array = new Array();
brand_new_array[10] = "random array value";
var result = brand_new_array.filter(function(e) { return e != undefined;})[0];
alert(brand_new_array.indexOf(result));
Travis J is right. The array in your example only contains one entry, but your use of jQuery.each() is making you think there are 10 entries because it iterates from 0 up to the highest index number of the array (defines the length). This is from the jQuery.each() API documentation.
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.
Going back to your example:
var brand_new_array = new Array();
brand_new_array[10] = "random array value";
This will result in only one console.log output:
for(var i in brand_new_array)
console.log(brand_new_array[i]);
This will result in 10 console.log outputs:
$(brand_new_array).each( function(i,e) { console.log(e) })
Similarly, this will result in 10 console.log outputs:
for (var i=0;i<brand_new_array.length;i++)
console.log(brand_new_array[i]);
If you really want to stick with using .each() then you can skip the undefined indices like so:
$(brand_new_array).each( function(i,e) {
if (this.hasOwnProperty(i)){ console.log(e) }
})
Filter the falsy items - including undifined:
var a=[1,2,"b",0,{},"",NaN,3,undefined,null,5];
var b=a.filter(Boolean); // [1,2,"b",{},3,5]
The length is 11 because the index starts at 0.
x[0] = undefined
x[1] = undefined
x[2] = undefined
x[3] = undefined
x[4] = undefined
x[5] = undefined
x[6] = undefined
x[7] = undefined
x[8] = undefined
x[9] = undefined
x[10] = "random array value"

JavaScript Two dimensional Array

I am creating javascript two dimensional array
code is :
var field_arr=[];
$(".dr").each(function(index){
Id=$(this).attr("id");
alert(dragId);
topPos=$("#"+ dragId).position().top;
left=$("#"+ dragId).position().left;
parentDiv=$("#"+dragId).parent().attr("id");
parentDiv= parentDiv.split('-');
paId=parentDiv[1];
field_arr[Id]=new Array();
field_arr[Id]['paId']=paId;
field_arr[Id]['top']=topPos;
field_arr[Id]['left']=left;
});
console.log(field_arr);
Output Is:
[undefined, [] left 140 paId "1" top 10
What is problem in It Any help Should be appreciated.
The problem is in the display method of your arrays. The information is there, but both alert and console.log will not show it to you because it is expected that the only interesting properties of arrays are the ones with numeric indexes.
In JavaScript, unlike PHP, objects are used as maps/associative arrays.
First to check that your information is actually there:
$(".dr").each(function(index){
var Id=$(this).attr("id");
console.log(Id, field_arr[Id]['paId'], field_arr[Id]['top'], field_arr[Id]['left']);
});
Now to make make the display methods work you can go about multiple ways, but the best one is to use objects instead:
var field_arr = Object.create(null); // replace with {} if you want to support IE8-
$(".dr").each(function(index){
var id = $(this).attr("id"); // added var to keep variable local
var drag = $("#"+dragId);
field_arr[id] = Object.create(null); // {}
field_arr[id]['paId'] = drag.parent().attr("id").split('-')[1];
field_arr[id]['top'] = drag.position().top;
field_arr[id]['left'] = drag.position().left;
});
console.log(field_arr);
Iterating over properties of objects is quite easy:
for (var id in field_arr) {
console.log(field_arr[id], field_arr[id]['paId'], 'etc');
}
Add a hasOwnProperty check if your object doesn't inherit from null (var obj = {} needs it, unlike var obj = Object.create(null))
you're storing values with a key string and its wrong because you declared your field_arr as a numerical array (well there's no such thing as associative array in javascript i think).
field_arr[Id] = new Array();
field_arr[Id]['paId']=paId; //this is wrong
You need to create an object to store in values as if they are associated with string keys. But literally they are object properties
redeclare it like this
field_arr[Id] = {}; //you create an object
field_arr[Id]['paId'] = paId; //create an object property named paId and store a value
field_arr[Id].paId = paId; //you can also access property paId like this
EDIT:
but to conform to you current code you can access your indexes using strings by accessing it like a property of an object. (Thanks to Tibos)
var field_arr=[];
...
...
field_arr[Id].paId = paId;

Adding to JSON array in JavaScript/jQuery

I have data being pulled in from various sources, each returning some form of JSON or similar, although, differently formatted each time. I need to get them all into one array, but I can't figure out how to do it.
The first set is an array like this:
[
Object {id="70", type="ab", dateadded="12345678"},
Object {id="85", type="ab", dateadded="87654321"}, ... more items ...
]
The second set is being pulled in from Facebook, and is like this:
[
Object {id="12341234234", created_time="12345678"},
Object {id="567856785678", created_time="87654321"}, ... more items ...
]
So, I need to alter the second set so that it has 'type', and it has 'dateadded' instead of 'created_time', and then I need to get this all into one array so it can be sorted on 'dateadded'.
How can I do this?
Use the first array's push() method:
// for each item in second array
firstArray.push(convert(item));
function convert(obj) {
// Convert obj into format compatible with first array and return it
}
Hope this helps.
Assuming you have actual valid JSON instead of what you quoted above:
var jsonOld = '[{"id":"70","type":"ab","dateadded":"12345678"},{"id":"85","type":"ab","dateadded":"87654321"}]',
jsonNew = '[{"id":"12341234234","created_time":"12345678"},{"id":"567856785678","created_time":"87654321"}]';
Then first parse these values into actual Javascript arrays:
var mainArr = JSON.parse(jsonOld),
newArr = JSON.parse(jsonNew);
(If you already have actual Javascript arrays instead of JSON strings then skip the above step.)
Then just iterate over newArr and change the properties you need changed:
for (var i = 0, il = newArr.length; i < il; i++) {
newArr[i].type = 'ab';
newArr[i].dateadded = newArr[i].created_time;
delete newArr[i].created_time;
}
And concatenate newArr into mainArr:
mainArr = mainArr.concat(newArr);
And sort on dateadded:
mainArr.sort(function(a, b) { return a.dateadded - b.dateadded; });
This will result in:
[{"id":"70","type":"ab","dateadded":"12345678"},
{"id":"12341234234","type":"ab","dateadded":"12345678"},
{"id":"85","type":"ab","dateadded":"87654321"},
{"id":"567856785678","type":"ab","dateadded":"87654321"}]
See example

Categories