Related
I face something I don't understand with an array. Indeed, I created an array I have filled with empty subArrays to obtain a 2D Matrix.
But when I manipulate the array it doesn't behave as I expected.
var arr = new Array(5);
arr.fill([]);
arr[2].push("third rank item");
console.log(arr);
//[ [ 'third rank item' ],
// [ 'third rank item' ],
// [ 'third rank item' ],
// [ 'third rank item' ],
// [ 'third rank item' ] ]
Every lights on this matter will be welcomed
This is the same old problem with arrays (and objects in general) being references rather than values.
Specifically, when you do arr.fill([]), you are taking that one single empty array and using that to fill the parent one.
It's like saying:
var arr = new Array(5);
arr[0] = arr[1] = arr[2] = arr[3] = arr[4] = [];
They all refer to the same array! So when you then go on to modify one of them, it looks like they're all modified (but really it's still the same one)
Unfortunately there's no simple way to assign an empty array to each one. You could do something like:
Array.apply(null, Array(5)).map(function() {return [];});
Essentially, create an (initialised) empty array of length 5 and map each (empty) value to a new [].
EDIT: Seems like I'm stuck in old times. As per #torazaburo's comment, you can use Array.from instead of Array.apply(null, Array(5)).map, like so:
Array.from( new Array(5), function() { return []; } );
As you can notice using array.fill you're filling the array with a reference to the same array,
if you want to instantiate each array index to an empty array a normal while loop will do:
var arr = [];
var n = 5
while(n--)
arr[n] = []
arr[2].push("third rank item");
console.log(arr);
Option 2:
if you have lodash package available, you can also use _.map as this is specificaly designed to loop through a sparse array (native map will skip non init values)
var arr =_.map(new Array(5), (x => []))
arr[2].push("third rank item");
console.log(arr)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
The eleventh line of the ECMA doc of Array.prototype.fill is clearly giving the reason for the mystery.
Repeat, while k < final
Let Pk be ToString(k).
Let setStatus be Set(O, Pk, value, true).
ReturnIfAbrupt(setStatus).
Increase k by 1.
Here "value" is just a reference received. And they are setting it as a property to array directly. That means all the filled arrays are just reference to a single array object.
It's happens cause of reference. Array is a type of object and object works on their references when you fill your array with [] or new Array() fill run only ones and put the same array in all indexes that's why when you update an sub-array all are updated.
Solution:
let arr = new Array(5).fill(0).map(ele => ele = []);
arr[2].push("something");
OR
let arr = Array.of([], [], [], []);
arr[2].push("something");
Result: as expected only 2 index of arr is updated.
With ES6 I recommend this method to create 2 or multidimensional arrays:
// create an M x N dimension grid and fill it with 0's
const myArray = [...Array(M)].map(r => [...Array(N)].map(r => 0));
Try this ,this is quick solution for you in one line.
var arr = new Array(5);
arr = Array.from(arr, x => []);
arr[2].push("third rank item");
console.log(arr);
You can try this,
var arr = new Array(5);
var i = 0;
while (i < arr.length)
arr.fill([], i++);
arr[2].push("third rank item");
console.log(arr);
A bit different:
let array= JSON.parse(JSON.stringify(new Array(N).fill([])));
or
let array=new Array(N).fill(null).map(()=>[]);
All the answers were correct and reasonable and so I decided to just try it out on my Chrome console. So the answer to the OP is that this is happening because you cannot do a direct fill.
If I understand part of the question is why can't you just do:
const grid = Array(5).fill([])
which is saying okay we have a structure like this:
//[ [ ],
// [ ],
// [ ],
// [ ],
// [ ] ]
And then you are saying go and fill the third one like so:
grid[2].push("third rank item");
But instead of seeing the third one filled with that string, you see all of them. It's simple, you are creating one single array and throwing it in at every location inside of grid. In memory there is only one inner array so you threw that string into that array, it would affect every index.
But I found some behavior slightly different than what I had expected.
If you write the following:
const grid = Array(3).fill([false, false, false]);
You would get the following:
(3) [Array(3), Array(3), Array(3)]
(3) [false, false, false]
(3) [false, false, false]
(3) [false, false, false]
Then if you run: grid[0].push(true), you will get the following:
(3) [Array(4), Array(4), Array(4)]
(4) [false, false, false, true]
(4) [false, false, false, true]
(4) [false, false, false, true]
So modifying one, modifies all, so that's why you cant just do a direct fill and then push as you did, instead you have to do the map() statement like so:
const grid = Array(3)
.fill(null)
.map(() => Array(3).fill(false));
And that will run the inner function three times and each time we generate a brand new and different array.
I am trying to replicate Python's insert at index functionality in Javascript.
var index = [0,1,2,3,4]
var nums = [0,1,2,2,1]
const target = new Array(nums.length);
for (let i=0; i<index.length; i++) {
target.splice(nums[i], 0, index[i]);
};
console.log(target);
This produces the following output:
[ 0, 4, 1, 3, 2, <5 empty items> ]
However, if I run the following code:
var index = [0,1,2,3,4]
var nums = [0,1,2,2,1]
const target = [] //new Array(nums.length);
for (let i=0; i<index.length; i++) {
target.splice(nums[i], 0, index[i]);
};
console.log(target);
This produces the following output:
[ 0, 4, 1, 3, 2 ]
What is going on? The second output is the one I desire.
new Array(nums.length) will fill the array with 'empty slots' for that number.
const target = [] Will be [ ]
const target = new Array(5) will be [, , , , ]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Array More info here.
when you initialize array like this :
const target = new Array(index.length);
This code will initiate "target" array with five empty box. in result it contain garbage value and it may affect your value after insertion.
But when you initialize like this :
const target = [];
This code will initiate "target" array without defining the size of it. it means you can insert n number of dynamic value inside this.
('n' denote the size of array)
So always use second option which is good practice to use javascript Array
I was testing giving index as a string to array. But I noticed that array converts string index to number.
for example: A["1"] turns to with A[1]
Can I give a number as a string to array index?
A["1"] = 1;
A["3"] = 3;
expected output:
[ 1: 1, 3: 3 ]
output:
[ <1 empty item>, 1, <1 empty item>, 3 ]
like as usual string index arrays;
A["a"] = 1;
A["c"] = 3;
output:
[ a: 1, c: 3 ]
I think you are confusing objects and arrays.
An array is just a list of items:
const array = [1,2,3,4,5]
// You can access its items by index:
array[0] => 1
array['1'] => 2
An object is a list of key-value pairs and keys are strings:
const obj = { a: 1, b: 2 }
// You can access its item values by key:
array.a => 1
array['b'] => 2
All property names are strings, So array like numeric property names really aren't any different from any other property names.
Javascript arrays cannot have "string indexes". A Javascript Array is exclusively numerically indexed. When you set a "string index", you're setting a property of the object.
A.a = '1' and A['a'] = '1' are equals.
So I think you need to use object. You can do this to get the same output.
function test() {
// A["1"] = 1;
// A["3"] = 3;
let A = {
1: 1,
3: 3
};
return A;
}
output
{ '1': 1, '3': 3 }
Everything in JavaScript is an Object.
Array too. It's just has some special properties and notation. An array is an Object where values are stored in key value pair. Keys are index of an array and values are elements.
For example:
const a = [1,2,4,6]
Object.keys(a) // iterating through keys will give you indexes [0,1,2,3]
There is just one catch. If you provide index of an array as non negative Integer, it will push value to array else all values will be associated as object.
So
a["a"]=1 // [1,2,4,6, a:1], length of an array will be 4 and key a can be accessed as property
Hope it helps.
I face something I don't understand with an array. Indeed, I created an array I have filled with empty subArrays to obtain a 2D Matrix.
But when I manipulate the array it doesn't behave as I expected.
var arr = new Array(5);
arr.fill([]);
arr[2].push("third rank item");
console.log(arr);
//[ [ 'third rank item' ],
// [ 'third rank item' ],
// [ 'third rank item' ],
// [ 'third rank item' ],
// [ 'third rank item' ] ]
Every lights on this matter will be welcomed
This is the same old problem with arrays (and objects in general) being references rather than values.
Specifically, when you do arr.fill([]), you are taking that one single empty array and using that to fill the parent one.
It's like saying:
var arr = new Array(5);
arr[0] = arr[1] = arr[2] = arr[3] = arr[4] = [];
They all refer to the same array! So when you then go on to modify one of them, it looks like they're all modified (but really it's still the same one)
Unfortunately there's no simple way to assign an empty array to each one. You could do something like:
Array.apply(null, Array(5)).map(function() {return [];});
Essentially, create an (initialised) empty array of length 5 and map each (empty) value to a new [].
EDIT: Seems like I'm stuck in old times. As per #torazaburo's comment, you can use Array.from instead of Array.apply(null, Array(5)).map, like so:
Array.from( new Array(5), function() { return []; } );
As you can notice using array.fill you're filling the array with a reference to the same array,
if you want to instantiate each array index to an empty array a normal while loop will do:
var arr = [];
var n = 5
while(n--)
arr[n] = []
arr[2].push("third rank item");
console.log(arr);
Option 2:
if you have lodash package available, you can also use _.map as this is specificaly designed to loop through a sparse array (native map will skip non init values)
var arr =_.map(new Array(5), (x => []))
arr[2].push("third rank item");
console.log(arr)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
The eleventh line of the ECMA doc of Array.prototype.fill is clearly giving the reason for the mystery.
Repeat, while k < final
Let Pk be ToString(k).
Let setStatus be Set(O, Pk, value, true).
ReturnIfAbrupt(setStatus).
Increase k by 1.
Here "value" is just a reference received. And they are setting it as a property to array directly. That means all the filled arrays are just reference to a single array object.
It's happens cause of reference. Array is a type of object and object works on their references when you fill your array with [] or new Array() fill run only ones and put the same array in all indexes that's why when you update an sub-array all are updated.
Solution:
let arr = new Array(5).fill(0).map(ele => ele = []);
arr[2].push("something");
OR
let arr = Array.of([], [], [], []);
arr[2].push("something");
Result: as expected only 2 index of arr is updated.
With ES6 I recommend this method to create 2 or multidimensional arrays:
// create an M x N dimension grid and fill it with 0's
const myArray = [...Array(M)].map(r => [...Array(N)].map(r => 0));
Try this ,this is quick solution for you in one line.
var arr = new Array(5);
arr = Array.from(arr, x => []);
arr[2].push("third rank item");
console.log(arr);
You can try this,
var arr = new Array(5);
var i = 0;
while (i < arr.length)
arr.fill([], i++);
arr[2].push("third rank item");
console.log(arr);
A bit different:
let array= JSON.parse(JSON.stringify(new Array(N).fill([])));
or
let array=new Array(N).fill(null).map(()=>[]);
All the answers were correct and reasonable and so I decided to just try it out on my Chrome console. So the answer to the OP is that this is happening because you cannot do a direct fill.
If I understand part of the question is why can't you just do:
const grid = Array(5).fill([])
which is saying okay we have a structure like this:
//[ [ ],
// [ ],
// [ ],
// [ ],
// [ ] ]
And then you are saying go and fill the third one like so:
grid[2].push("third rank item");
But instead of seeing the third one filled with that string, you see all of them. It's simple, you are creating one single array and throwing it in at every location inside of grid. In memory there is only one inner array so you threw that string into that array, it would affect every index.
But I found some behavior slightly different than what I had expected.
If you write the following:
const grid = Array(3).fill([false, false, false]);
You would get the following:
(3) [Array(3), Array(3), Array(3)]
(3) [false, false, false]
(3) [false, false, false]
(3) [false, false, false]
Then if you run: grid[0].push(true), you will get the following:
(3) [Array(4), Array(4), Array(4)]
(4) [false, false, false, true]
(4) [false, false, false, true]
(4) [false, false, false, true]
So modifying one, modifies all, so that's why you cant just do a direct fill and then push as you did, instead you have to do the map() statement like so:
const grid = Array(3)
.fill(null)
.map(() => Array(3).fill(false));
And that will run the inner function three times and each time we generate a brand new and different array.
I am trying to add array of arrays dynamically in javascript. I need the data in the following format -
var dataArray = [[],[],[],.....,[]];
How can I initialize this kind of array? Suppose if I have three arrays to be added, I can initialize as follows -
var dataArray = [[],[],[]];
This will accept only three records to be added. But, what should I do in case of adding large number of arrays? Here I cannot know the amount of data I get as input.
I have tried using concat() and merge() methods, these are adding contents directly in to a single array, but that is not what I wanted.
Can any one please help me out on this?
You can build or add an array into an array like this:
var dataArray = [];
dataArray.push([1,2,3]);
dataArray.push([3,4,5]);
console.log(dataArray); // [[1,2,3], [3,4,5]]
Or, if you want to add elements to the sub-arrays:
var dataArray = [];
dataArray.push([1,2,3]);
dataArray.push([3,4,5]);
dataArray[0].push(4);
dataArray[1].push(9);
console.log(dataArray); // [[1,2,3,4], [3,4,5,9]]
You initialize a sub-array by assigning an array to the element of the outer array. You can then use array operations directly on the sub-array element:
// create a sub-array element
dataArray[2] = [];
dataArray[2].push(8);
dataArray[2].push(7);
console.log(dataArray[2]); // [8,7]
console.log(dataArray); // [[1,2,3,4], [3,4,5,9], [8,7]]
The key thing it appears you don't understand is that an array of arrays is just that. It's an outer array where each element in the outer array is itself an array. You use ordinary array methods to operate on either the outer array or the inner arrays. To operate on an inner array, you fetch that element from the outer array and then just treat it as an array. For example:
var dataArray = [];
dataArray.push([1,2,3]);
dataArray.push([3,4,5]);
console.log(dataArray); // [[1,2,3], [3,4,5]]
var innerArray = dataArray[0];
console.log(innerArray); // [1,2,3]
innerArray.push(12);
console.log(innerArray); // [1,2,3,12]
innerArray.legnth = 2;
console.log(innerArray); // [1,2]
innerArray.push(9,8,7);
console.log(innerArray); // [1,2,9,8,7]
innerArray.splice(1,2);
console.log(innerArray); // [1,8,7]
You have wrote "I am trying to add array of arrays dynamically in javascript"
The simple way is using Array.prototype.push method:
var arr1 = [1,2], arr2 = [3,4], arr3 = [5,6], arr4 = [7,8], arr5 = [9,10],
dataArray = [];
[].push.apply(dataArray, [arr1, arr2, arr3, arr4, arr5]);
console.log(JSON.stringify(dataArray, 0, 4));
The console.log output:
[
[
1,
2
],
[
3,
4
],
[
5,
6
],
[
7,
8
],
[
9,
10
]
]
There are tons of way you can do this. A simple one could be
var arrayN = n => Array(n).fill([])
document.write("<pre>" + JSON.stringify(arrayN(10)) + "</pre>")
On another thinking if you already have arrays of arrays then the most simplified way of concatenating them in place should be using the new spread operator like;
var arr = [[1,2,3],[1,3,5]],
brr = [[3,2,1],[7,8,9]];
arr.push(...brr);
document.write("<pre>" + JSON.stringify(arr) + "</pre>");