This question already has answers here:
What is "undefined x 1" in JavaScript?
(5 answers)
Closed 4 years ago.
Some of the common things asked is , why Array appears to be zero even if there's content inside. My question now is a bit weird. Why am I seeing
(66) [empty × 65, {…}]
in chrome console even if the array has at least 1 array value inside ?
That is what you see when you have a sparse array - with some indicies defined, but some indicies lower than the defined indicies not defined (or ever assigned to), for example:
const arr = [];
arr[65] = 'foo';
console.log(arr);
(look at the results of these two snippets in the browser console, snippet console won't be visible)
Note that this is different from having literal undefined values in the first 65 indicies, which will be shown as undefined rather than empty:
const arr = new Array(65).fill(undefined);
arr[65] = 'foo';
console.log(arr);
Sparse arrays are almost never a good idea, though - if you ever see this, it's an indication that you should probably fix the code to avoid the sparse arrays. Consider another structure instead, perhaps an object with numeric keys:
const obj = {
'65': 'foo'
};
A programmer will expect an array to be an ordered collection of elements - what would it mean for a collection to not have any value at all at the top of an ordered collection (not just undefined, but not any value)? It just doesn't make much sense - while possible, such constructs are not what arrays are for.
As Kaiido notes, empty elements will not be iterated over with array methods like forEach:
const arr = [];
arr[65] = 'foo';
arr.forEach((item, i) => {
console.log(item, i);
});
Which makes a bit of sense, but it's still unintuitive (would you have been sure of this without trying the code yourself or looking up the spec?). One would usually expect the first iteration of a forEach callback to have an index of 0.
It's just 65 empty elements, it's can be if you create array like this:
const arr = Array(65);
arr.push(1);
console.log(arr)
or in new Array set new element on some index;
const arr = [];
arr[65] = 1;
Related
I am trying to maintain the order of an array with mixed key types. The array contains mostly keys represented by string values -- but if you enter a numbered key it goes to the front. How can I force a key which is a number to be a string type?
E.g.
array = [];
array["one"] = "some data";
array["two"] = "some more data";
array["3"] = "this should not be the first element";
How can I make "3" a string type to prevent it from moving to the top of the index?
Oh wow did you ever open multiple cans of worms.
Javascript arrays are a special type of Javascript objects, and like all Javascript objects they can have arbitrary string properties:
const foo = [];
foo["bar"] = "hi";
However that string is a property of the array object, not an item in the array:
foo.forEach(console.log); // logs nothing
You can still access it like any other object property:
console.log(foo["bar"]); // "hi"
But it won't show up in the usual iterative constructs like c-style for loops or the map/forEach array methods.
The line in your example
array["3"] = "this should not be the first element";
is very different however, because of Javascript's playing fast and loose with type conversions this actually sets the string to the 4th slot in the array:
const bar = [];
bar["3"] = "oops!"; // equivalent to bar[3] = "oops!"
console.log(bar); // [empty x 3, "oops!"]
This piece of it is actually a good thing (other than the implicit conversion part) rather than a problem: sometimes you need a sparse array and JS supports those. Iterating it will only produce the one element:
bar.forEach((item, index) => console.log(item, index)); // ["oops", 3]
Note though that the string has the correct index of 3, and can be accessed that way even though there's nothing "in front" of it:
bar[3]; // "oops"
So the first two assignments in your example create properties on the array object, and the third assignment is the only one that actually adds an item to the array, at the 4th index (there's nothing at the first 3).
What you seem to want as Reese Casey suggests, is a plain object:
const foo = {}; // curly
foo["some string"] = "whatever";
However now the properties are basically unordered. If you want them to be in a guaranteed specific order you do want an array, but all your indicies will need to be integers, and should be sequential. You can achieve this easily by using the .push method:
foo = [];
foo.push("something");
foo.push("something else");
Now foo will have two elements, in the correct order, and index 0 and 1 respectively.
Update based on comment on the other answer:
I want some of the data to be ordered, and the rest of the data to follow
This can be accomplished through object destructuring:
const responseFromDB = {
oneKeyICareAbout: 3,
anotherKeyICareAbout: 2,
foo: 6,
bar: 7,
};
const {
oneKeyICareAbout,
anotherKeyICareAbout,
*rest,
} = responseFromDB;
const stuffToDisplay = [
oneKeyICareAbout,
anotherKeyICareAbout,
...Object.values(rest),
]; // [3, 2, 6, 7]
And at least the destructured stuff you put in the array will be ordered because by doing so you've ordered it.
Javascript arrays cannot have string indexes. This is actually working incorrectly as the index is adding a property to the array object.
Changing to an object makes more sense for this.
EDIT: Whilst below its mentioned you can have string indexes you are not actually using the array by doing so. The answer by Jared Smith goes into much more detail as to why.
The other answers explain what is happening with your array-object mixture. For having an indexable thing which can reproduce the original order, you can use a Map:
The Map object holds key-value pairs and remembers the original insertion order of the keys.
array = new Map();
array.set("one","some data");
array.set("two","some more data");
array.set("3","this should not be the first element");
console.log("Test of get:",array.get("two"));
console.log("Test of order:");
for(let entry of array)
console.log(entry);
This question already has answers here:
How do you clone an array of objects in JavaScript?
(40 answers)
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 4 years ago.
OK.
Let me try to say this in some sort of comprehensible manner. I'm an amateur programmer writing my own take on a neural network in javascript. (Without having seen the code for a neural network before)
I was having problems with an array changing when I was trying to change a copy of the array. (Not the original)
I soon realized after rereading what I'd written that when you assign an identifier to an array it doesn't make the identifier a new object with a copy of the array. Instead, it makes a reference to the original array object, for example:
var arr = [1,2,3];
var arr2 = arr;
arr2[0] = 9;
alert(arr);
//Alerts "9,2,3"
With this mind, I googled and found a quick solution:
var arr = [1,2,3];
var arr2 = arr.slice();
arr2[0] = 9;
alert(arr);
//Alerts "1,2,3"
So I changed this in my actual project expecting to see my work completed, but no! I was getting the exact results as before where my array was changing even though it was not supposed to.
After much effort at debugging, I finally worked out that the problem here is that I have a large array of subarrays, which in turn have subarrays.
In code this looks like:
var arr = [
[[1],[2]],
[[4],[5]],
[[7],[8]]
];
As you can see, there is one big array that contains 3 smaller arrays, each of which contains two even smaller arrays, each of which contains a number.
In my project, it's more complicated than this and has a couple more layers but this is a decent representation.
So what did I expect to happen?
var arr = [
[[1],[2]],
[[4],[5]],
[[7],[8]]
];
var other = arr.slice();
other[0][0][0] = "Uh Oh";
alert(arr);
//Outputs "1,2,3,4,5,6,7,8"
What actually happened?
alert(arr);
//Outputs "Uh Oh,2,3,4,5,6,7,8"
Why does this happen?
How can I fix it?
Thanks in advance.
Try following
var arr = [
[[1],[2]],
[[4],[5]],
[[7],[8]]
];
var other = JSON.parse(JSON.stringify(arr));
other[0][0][0] = "Uh Oh";
console.log(arr);
Reasoning
arr.slice() creates different reference for the array, however, any child object/array will still continue to hold the same reference. But you need to change the references of the child object/array as well, for that convert it into a string and then back to object - It will create different references for all the objects.
This question already has answers here:
Show original order of object properties in console.log
(6 answers)
Closed 4 years ago.
I'm dealing with a bug that is a result of the order of properties in an object. Yes perhaps it would be better to use an array in this case but that's not the point of this question.
When I print out the object to the console, it is automatically sorted for me, which is annoying in this case.
In this screenshot you see that the immediate preview (grey text on top) is not sorted, but when you open the object, it is sorted.
The same goes for the inner properties. Take note of the item "0.180 - 0.299" in the above screenshot, and how it's properties are not sorted properly in the immediate preview. But when we open them they are:
Usually this is convenient but I was wondering if there was a way to disable this feature temporarily.
Here's how it looks in the React dev tools:
I think it may actually be how chrome stores and fetches the object. I can't seem to prove that it actually stores the order of the object. What made you determine that it was storing it differently?
const a = { 2: {d: 1, e:1, a:1}, 3: {d: 1, e:1, a:1}, 1: {d: 1, e:1, a:1}};
const keys = Object.keys(a);
for(let key of keys){
console.log(key)
console.log(a[key]);
for(let embeddedKey of Object.keys(a[key])){
console.log(embeddedKey);
}
}
console.log(JSON.stringify(a));
console.log('---- underscore stuff ----');
_.each(a, (val, key) => {
console.log(key);
console.log(val)
_.each(val, (embeddedVal, embeddedKey) => {
console.log(embeddedKey);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Edit: Changed the script to show that it is in fact already sorted when numbers are used, but not when strings are used.
The solution is to therefore use strings as keys for when you need to preserve the order. You will need to use some special formatting so it's not seen as a number.
let a = {"2n": {b: 1, a: 1}, "1n": {d: 1, c:1}};
console.log(a);
Messing around with some JavaScript and what I thought should have produced a bug happened to work out as intended. [].join() seems to flatten a nested array of any depth.
var arr = [['a'], ['b'], ['c']];
arr.join('-'); // => 'a-b-c'
And even var arr = [['a'], [[[[[[[[['b']]]]]]]]], ['c']]; returns the exact same result as above.
Which is especially odd since it returns the expected (erroneous) behavior with objects:
var arr = [{}, {}, {}];
arr.join('-'); // => '[object Object]-[object Object]-[object Object]'
Curious if this is a feature of the latest chrome (53.0.2785.116), intended, or a bug?
This is a result of the default toString behavior on ['a'] resulting in 'a'. It follows that [['a']].toString() also results in 'a'. Playing around with this in the console should make this clear. You would see a difference if your arrays had multiple elements, because ['a', 'b'].toString() would result in 'a,b'.
For each element in the array that's being joined, the first thing that happens is that the element is coerced to a string. Something like
[[[[["b"]]]]]
when coerced to a string will just be
"b"
So when you do the join, the constituent elements will be forced to be strings before the join process does its thing of gluing the elements together.
When I do something like this:
var o = new Array();
o[20] = true;
o[1000] = true;
o[4000] = true;
Is it reasonable to expect that only 3 elements will be allocated or can the implementation decide to suddenly allocate something with 4000 elements?
The reason I'm asking is that when I do this I see in firebug an indication that there are actually 4000 undefined in o. are they really there?
Now that we know that o is an Array, I can answer you precisely.
No, the elements will not be 'allocated' or 'created'.
When you make an assignment of an index property to an Array object, which is greater than the actual length of the array two things happen:
The index named property is created
The length property is incremented, to be the index + 1
For example:
var o = [];
o[4000] = true;
o.hasOwnProperty(0); // false, the property doesn't exist
o.hasOwnProperty(1); // false
o.hasOwnProperty(4000); // true, the property exist
As you can see, the hasOwnProperty method returns false when we test the presence of the 0 or 1 properties, because they don't exist physically on the object, whereas it returns true for 4000, the property that was created.
When Firebug detects that the object being printed in the console is an array-like object, it will simply make a loop, showing each of the index values from 0 to length - 1.
Firebug detects array-like objects simply by looking if they have a length property whose its value is an unsigned 32-bit integer (less than 2^32 - 1), and if they have a splice property that is a function, for example, the following object will be detected and printed as an Array on the Firebug's console:
console.log({length:3, splice:function(){}});
// Firebug will log: `[undefined, undefined, undefined]`
a) That code is not valid. You need either var o = {}; // object or var o = []; // array.
b) In the first case, the object is sparse. In the second, it may depend on the interpreter; in modern browsers it is also sparse. Try o[99999999] = "hi"; and see how much memory your browser does or does not allocate. If it does not go up by at least 10MB, your arrays are sparse.
I think this one answers the question.
Are Javascript arrays sparse?
And according to that one, arrays are spares, thats is, if you use for(item in array) you only get 3 items, not 4000 but if you use array.length it will take the larges integer value and return one larger, look here:
http://www.crockford.com/javascript/survey.html
Linkpad will use a for(item = 0; item < array.length; item++) and that one will return undefined for any index that is not present in the array.
/*
On the other hand, if you have a very large index any array manipulation will have to loop
through each index- it won't skip from one defined item to the the next defined item.
*/
var A= [];
A[0]= 'a';
A[10]= 'b';
A[4000000]= 'c';
alert(A.filter(function(itm){
return itm!= undefined;
}));
Javascript will generate all elements inbetween, always. You may want to use o.length to verify the length of the array. It will return 4000 and not 3.