Javascript: is there a data structure, "matrix", thing[x][y]? - javascript

I don't know the terminology but I want to get it simpler:
var thingTopic1 =['hello','hallo', ..., 'hej'];
var thingTopic2 =['a','b',...,'c'];
...
var thingTopic999 =['x,'y',...,'?'];
so I want to access the data like thing[para1][para2], is there some ready data structure for it or do I need to create messy function with the things? Please, note that sizes of things differ.

You can have arrays of arrays, and the size of each row can be different.
var matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[0]
];
The variable "matrix" will refer to an array with length 4. The syntax you use to refer to (say) the "5" in the second row is exactly what you suggested:
var theFive = matrix[1][1];
You can "build" a matrix like that incrementally of course.
var matrix = [];
for (var i = 1; i < 10; ++i) {
var row = ~~((i - 1) / 3);
if (!matrix[row]) matrix[row] = [];
matrix[row][(i - 1) % 3] = i;
}
matrix.push([0]);
When you set an integer-indexed "property" of an Array instance, Javascript makes sure that the "length" property of the array is updated. It does not allocate space for "holes" in the array, so if you set element number 200 first, there's still just one thing in the array, even though "length" would be 201.

No, there is no data structure for that, but you can easily accomplish it by combining arrays.
You can create an array that contains arrays, which is called a jagged array:
var thing = [
['hello','hallo','goddag','guten tag','nuqneH','hej'],
['a','b','c','d','e','f','g','h','i','j'],
['x,'y','z']
];
Notice how the inner arrays can have different length, which is where the term "jagged" comes from.

You can take advantages from OOP of ES6 :
class Matrix extends Array {
constructor(...rows) {
if(rows.some( r => !Array.isArray(r)))
throw new TypeError('Constructor accepts only rows as array')
super(...rows)
}
push(...rows) {
if(rows.some( r => !Array.isArray(r)))
throw new TypeError('Push method accepts array(s)')
super.push(...rows)
}
}
Use case 1:
Use case 2 :

Related

Duplicate an array an arbitrary number of times (javascript)

Let's say I'm given an array. The length of this array is 3, and has 3 elements:
var array = ['1','2','3'];
Eventually I will need to check if this array is equal to an array with the same elements, but just twice now. My new array is:
var newArray = ['1','2','3','1','2','3'];
I know I can use array.splice() to duplicate an array, but how can I duplicate it an unknown amount of times? Basically what I want is something that would have the effect of
var dupeArray = array*2;
const duplicateArr = (arr, times) =>
Array(times)
.fill([...arr])
.reduce((a, b) => a.concat(b));
This should work. It creates a new array with a size of how many times you want to duplicate it. It fills it with copies of the array. Then it uses reduce to join all the arrays into a single array.
The simplest solution is often the best one:
function replicate(arr, times) {
var al = arr.length,
rl = al*times,
res = new Array(rl);
for (var i=0; i<rl; i++)
res[i] = arr[i % al];
return res;
}
(or use nested loops such as #UsamaNorman).
However, if you want to be clever, you also can repeatedly concat the array to itself:
function replicate(arr, times) {
for (var parts = []; times > 0; times >>= 1) {
if (times & 1)
parts.push(arr);
arr = arr.concat(arr);
}
return Array.prototype.concat.apply([], parts);
}
Basic but worked for me.
var num = 2;
while(num>0){
array = array.concat(array);
num--}
Here's a fairly concise, non-recursive way of replicating an array an arbitrary number of times:
function replicateArray(array, n) {
// Create an array of size "n" with undefined values
var arrays = Array.apply(null, new Array(n));
// Replace each "undefined" with our array, resulting in an array of n copies of our array
arrays = arrays.map(function() { return array });
// Flatten our array of arrays
return [].concat.apply([], arrays);
}
console.log(replicateArray([1,2,3],4)); // output: [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
What's going on?
The first two lines use apply and map to create an array of "n" copies of your array.
The last line uses apply to flatten our recently generated array of arrays.
Seriously though, what's going on?
If you haven't used apply or map, the code might be confusing.
The first piece of magic sauce here is the use of apply() which makes it possible to either pass an array to a function as though it were a parameter list.
Apply uses three pieces of information: x.apply(y,z)
x is the function being called
y is the object that the function is being called on (if null, it uses global)
z is the parameter list
Put in terms of code, it translates to: y.x(z[0], z[1], z[2],...)
For example
var arrays = Array.apply(null, new Array(n));
is the same as writing
var arrays = Array(undefined,undefined,undefined,... /*Repeat N Times*/);
The second piece of magic is the use of map() which calls a function for each element of an array and creates a list of return values.
This uses two pieces of information: x.map(y)
x is an array
y is a function to be invoked on each element of the array
For example
var returnArray = [1,2,3].map(function(x) {return x + 1;});
would create the array [2,3,4]
In our case we passed in a function which always returns a static value (the array we want to duplicate) which means the result of this map is a list of n copies of our array.
You can do:
var array = ['1','2','3'];
function nplicate(times, array){
//Times = 2, then concat 1 time to duplicate. Times = 3, then concat 2 times for duplicate. Etc.
times = times -1;
var result = array;
while(times > 0){
result = result.concat(array);
times--;
}
return result;
}
console.log(nplicate(2,array));
You concat the same array n times.
Use concat function and some logic: http://www.w3schools.com/jsref/jsref_concat_array.asp
Keep it short and sweet
function repeat(a, n, r) {
return !n ? r : repeat(a, --n, (r||[]).concat(a));
}
console.log(repeat([1,2,3], 4)); // [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
http://jsfiddle.net/fLo3uubk/
if you are inside a loop you can verify the current loop index with the array length and then multiply it's content.
let arr = [1, 2, 3];
if(currentIndex > arr.length){
//if your using a loop, make sure to keep arr at a level that it won't reset each loop
arr.push(...arr);
}
Full Example:
https://jsfiddle.net/5k28yq0L/
I think you will have to write your own function, try this:
function dupArray(var n,var arr){
var newArr=[];
for(var j=0;j<n;j++)
for(var i=0;i<arr.length;i++){
newArr.push(arr[i]);
}
return newArr;
}
A rather crude solution for checking that it duplicates...
You could check for a variation of the length using modulus:
Then if it might be, loop over the contents and compare each value until done. If at any point it doesn't match before ending, then it either didn't repeat or stopped repeating before the end.
if (array2.length % array1.length == 0){
// It might be a dupe
for (var i in array2){
if (i != array1[array2.length % indexOf(i)]) { // Not Repeating }
}
}

multiple arrays into one array

I need help with the five.myArraysCombined property.
I need it to equal just 1 array (which it currently does in fiddle) and I need it to NOT add any numbers together. (so each number in the array shouldn't be over 20, just like no number in the other arrays are over 20)
http://jsfiddle.net/Dc6HN/1/
For example, if the five arrays are like this
five.myArray1 = [7,2,9,19,3];
five.myArray2 = [6,18,8,1,7];
five.myArray3 = [7,19,4,8,2];
five.myArray4 = [11,9,1,14,5];
five.myArray5 = [3,18,8,9,2];
then the all those arrays combined should be like this
five.myArraysCombined = [7,2,9,19,3,6,18,8,1,7,7,19,4,8,2,11,9,1,14,5,3,18,8,9,2];
Relevant code :
function theNumberClass() {
this.myArray = [[],[],[],[],[]];
this.myArraysCombined = [];
}
var five = new theNumberClass();
function prePickNumbers(objName, theNum, theSumNum, theMaxNum, theMinNum) {
var zzz = [];
for (var x = 0; x < theNum; x += 1) {
pickNumbers(objName.myArray[x], theNum, theSumNum, theMaxNum, theMinNum);
zzz += objName.myArray[x];
}
objName.myArraysCombined.push(zzz);
}
prePickNumbers(five, 5, 40, 20, 1);
My latest attempt was with var zzz and then pushing it to the property, but when I do that it adds up the numbers in the array at times, which is not what I need.
I've also tried several attempts using the .concat(), but it seems to turn it into a string and sometimes also adds up the numbers.
Suppose you have those arrays :
var a = [1, 2, 3]
var b = [4, 5, 6]
var c = [8]
Then you can get a merge of all those with
var all = [].concat.apply([],[a,b,c])
or with
var all = [a,b,c].reduce(function(merged, arr){ return merged.concat(arr) })
In both cases you get
[1, 2, 3, 4, 5, 6, 8]
The first solution is simpler, the second one is more extensible if you want, for example, to remove duplicate or do any kind of filtering/transformation.
I would guess that the issue is the "+=" operator. This operator is used to sum values, not add new elements to an array. Take the following line of code as an example:
zzz += objName.myArray[x];
What I am guessing is that "myArray[x]" is getting added to the value of zzz instead of getting appended to the end of the array. When adding elements to an array in javascript, push is the best option. A better way to write this line is:
zzz.push(objName.myArray[x]);
The question was a bit confusing so I'm not sure if this is what you are looking for but hopefully it will help anyways.
five.reduce(function(o,n){return o.concat(n)},[])
This will reduce the array to a single value, in this case an array of numbers. You can look up Array.reduce() on MDN for more info.
After many hours trying all suggestions left on this thread and another one, and trying multiple other things. I think I finally found a very simple way to do this. And it's the only way I tried that works 100% like I want.
http://jsfiddle.net/Dc6HN/2/
function prePickNumbers(objName, theNum, theSumNum, theMaxNum, theMinNum) {
for (var x = 0; x < theNum; x += 1) {
pickNumbers(objName.myArray[x], theNum, theSumNum, theMaxNum, theMinNum);
objName.myArraysCombined.push(objName.myArray[x]);
}
objName.myArraysCombined = objName.myArraysCombined.toString();
objName.myArraysCombined = objName.myArraysCombined.split(',');
}

Undo sort on sorted array in javascript

I have an array. I sort it.
I get a second array which is already sorted based on the first one.
I need to reverse the sorting on the second array.
For example, if the first array (unsorted) is: [9, 5, 3, 0, 2] then I want to to sort it, so that it becomes [0, 2, 3, 5, 9].
Then I receive the second array sorted based on the first one, for example ["home", "car", "train", "pc", "mouse"]. I need it to become ["mouse, "pc", "train", "home", "car"].
I can't make a copy of the array.
I have the following code:
//data_r is an array with values
var i = 0;
var sort_order = new Array();
data_r.sort(function (a,b) {
var res = a[0] - b[0];
sort_order[i] = res;
i++;
return res;
});
In the end, the the sort_order array will contain the actions performed when we sorted items. If I want to sort a second array exactly the same way as the first then I can do the following:
//data_x is an array with values
var i = 0;
data_x.sort(function (a,b) {
i++;
return sort_order[i-1];
});
Now the data_x array is sorted exactly the same way as the data_r array.
How can I undo sort on the data_r array?
The following code is incorrect:
var unsort = new Array();
for(var i = 0; i < data_r.length; i++)
unsort[i] = sort_order[i]*(-1);//-1 so we perfom the oposite action
Your premise here is flawed.
In the end, the sort_order array contains the actions performed when we sorted items.
No, it doesn't; it contains a log of the comparisons performed by the Javascript Array.sort function. The actions it took in response to those comparison results are private to it.
If I want to sort a second array exactly the same way as the first then I can do the following:
This is not guaranteed to work. Even if the two arrays are the same size, Array.sort may not always compare the same elements in the same order each time it's called - it's possible that it's using a randomized algorithm, that it performs comparisons based on other data that are internal to the interpreter, or that it switches between multiple entirely different sort algorithms under some circumstances.
While this code may work for you, right now, in your current web browser, it is likely to fail in surprising ways in other circumstances (possibly in future browsers). Do not use this technique in production code.
The question is, how can i unsort the data_r array?
Make a copy of the array before you sort it.
Storing res[i] = a - b is like journaling the sort() algorithm - but what if it used a random pivot?
This code is inherently unreliable unless you write sort() yourself. It's also inefficient.
A better approach, one that will solve both your needs, is to create an array of indices and sort that. This is trivial to invert. Then you can implement a permute function that takes an array of indices, and it achieves a sort or unsort, depending on the input.
If x is from 0:n-1, create an array sort_i of same size, then initialize each sort_i[i] = i.
for(var i = 0; i < n; i++)
sort_i[i] = i;
Then
sort_i.sort(function (a,b) { return x[a] - x[b]; });
Now you have the indices. To apply to x:
for(var i = 0; i < n; i++)
sort_x[i] = x[sort_i[i]];
To unsort it, first invert the indices
for(var i = 0; i < n; i++)
unsort_i[sort_i[i]] = i;
Then apply the indices. Exercise left to question asker.
This approach of sorting an array of integer indices is needed when you don't want to move the original elements around in memory (maybe they are big objects), and many other circumstances. Basically you are sorting pointers. The result is an index to the data, and a reverse index.
See #duskwuff's answer on why your approach doesn't work.
Instead, just introduce a mapping between the original data and the sorted data.
{0:2, 1:3, 2:1, 3:0}
Which means the first element became the third, the second became the last and so on. Below we'll use an array instead of an object.
Why does this map help? You can sort it like another dataset by just using the indizes in it as pointers to the data you're going to compare. And you can apply the mapping easily on other datasets. And you can even reverse that mapping very easily. See it in the code:
// data_r, data_x are arrays with values
var l = data_r.length;
var sort_order = new Array(l);
for (var i=0; i<l; i++) sort_order[i] = i; // initialised as 1-1 mapping
// change the sort_order first:
sort_order.sort(function (a,b) {
// a and b being indices
return data_r[a] - data_r[b];
});
// Making a new, sorted array
var data_x_sorted = new Array(l);
for (var i=0; i<l; i++)
data_x_sorted[ sort_order[i] ] = data_x[i]; // put it to sorted position
If you want to sort the data_x array itself, just use the "apply" algorithm which I showed for data_r.
The question is, how can I undo sort on the data_r array?
Either don't sort it at all, and just make a copy of it which gets sorted (or do nothing at all).
Or use the sort_order to reverse it. You just would need to swap i and newIndex (sortOrder[i]) everywhere. Example for building a new, "unsorted" (old-order) array:
var unsorted = new Array(l);
for (var i=0; i<l; i++)
unsorted[i] = data_r[ sort_order[i] ]; // take it from its new position
While this question is 8 years old at this point, I came across it when trying to find the same solution to the problem and I was unable to find a suitable, performant, and intuitive way of doing so, so I wrote one myself.
Please take a look at the sort-unwind library. If ranks is a list of indexes that would rank an array in order...
import unwind from 'sort-unwind'
const suits = ['♥', '♠', '♣', '♦']
const ranks = [2, 0, 3, 1]
const [sortedSuits, tenet] = unwind(ranks, suits)
// sortedSuits <- ['♠', '♦', '♥', '♣']
// unwind <- [1, 3, 0, 2]
You can then use the tenet variable that's returned to unsort an array and restore the original ordering.
const names = ['spades', 'diamonds', 'hearts', 'clubs']
const [tenetNames, tenetRanks] = unwind(tenet, names)
// tenetNames <- ['hearts', 'spades', 'clubs', 'diamonds']
// tenetRanks <- [2, 0, 3, 1]
The sort function just returns a number which can be positive,zero, or negative telling it if the current element goes before,has same weight, or goes after the element it is comparing it too. I would imagine your sort order array is longer than your data_r array because of the number of comparisons you make. I would just make a copy of data_r before you sort it and then set data_r equal to that array when you want it unsorted.
If you have a lot of these arrays to maintain, it might be as well to
convert array1 into an array of objects, each one containing the value
and its original position in the array. This keeps everything together
in one array.
var array1 = [9, 5, 3, 0, 2];
var array2 = ["home", "car", "train", "pc", "mouse"];
var sort = function(array){
var indexed_objects = array.map(function(value, index){
return {index: index, value: value};
});
indexed_objects.sort(function(a,b){
return a.value <= b.value ? -1 : 1;
});
return indexed_objects;
};
var sorted1 = sort(array1);
sorted1; // [{index: 3, value:0}, {index: 4, value: 2}, ...]
And now, given an array of sorted objects, we can write a function to
unsort any other array accordingly:
var unsort = function(array, sorted_objects){
var unsorted = [];
sorted_objects.forEach(function(item, index){
unsorted[item.index] = array[index];
});
return unsorted;
};
var array2_unsorted = unsort(array2, sorted1);
array2_unsorted; // ["mouse", "pc", "train", "home", "car"]
v1 = [0,1,2,3,4,5,6]
q = v1.length
b = []
for(i=0;i<q;i++){
r = parseInt(Math.random()*v1.length)
b.push(v1[r])
a = v1.indexOf(v1[r])
v1.splice(a,1)
}

What is the way of declaring an array in JavaScript?

I'm just learning JavaScript and it seems like there are a number of ways to declare arrays.
var myArray = new Array()
var myArray = new Array(3)
var myArray = ["apples", "bananas", "oranges"]
var myArray = [3]
What are their difference, and what are the preferred ways?
According to this website the following two lines are very different:
var badArray = new Array(10); // creates an empty Array that's sized for 10 elements
var goodArray= [10]; // creates an Array with 10 as the first element
As you can see these two lines do two very different things. If you
had wanted to add more than one item then badArray would be
initialized correctly since Javascript would then be smart enough to
know that you were initializing the array instead of stating how many
elements you wanted to add.
Is what the authors trying to say is Array(10) creates an array with precisely 10 elements and [10] creates an array of undefined size with the 0th element being 10? Or what does this mean?
In your first example, you are making a blank array, same as doing var x = []. The 2nd example makes an array of size 3 (with all elements undefined). The 3rd and 4th examples are the same, they both make arrays with those elements.
Be careful when using new Array().
var x = new Array(10); // array of size 10, all elements undefined
var y = new Array(10, 5); // array of size 2: [10, 5]
The preferred way is using the [] syntax.
var x = []; // array of size 0
var y = [10] // array of size 1: [1]
var z = []; // array of size 0
z[2] = 12; // z is now size 3: [undefined, undefined, 12]
The preferred way is to always use the literal syntax with square brackets; its behaviour is predictable for any number of items, unlike Array's. What's more, Array is not a keyword, and although it is not a realistic situation, someone could easily overwrite it:
function Array() { return []; }
alert(Array(1, 2, 3)); // An empty alert box
However, the larger issue is that of consistency. Someone refactoring code could come across this function:
function fetchValue(n) {
var arr = new Array(1, 2, 3);
return arr[n];
}
As it turns out, only fetchValue(0) is ever needed, so the programmer drops the other elements and breaks the code, because it now returns undefined:
var arr = new Array(1);
To declare it:
var myArr = ["apples", "oranges", "bananas"];
To use it:
document.write("In my shopping basket I have " + myArr[0] + ", " + myArr[1] + ", and " + myArr[2]);
There are a number of ways to create arrays.
The traditional way of declaring and initializing an array looks like this:
var a = new Array(5); // declare an array "a", of size 5
a = [0, 0, 0, 0, 0]; // initialize each of the array's elements to 0
Or...
// declare and initialize an array in a single statement
var a = new Array(0, 0, 0, 0, 0);
If you are creating an array whose main feature is it's length, rather than the value of each index, defining an array as var a=Array(length); is appropriate.
eg-
String.prototype.repeat= function(n){
n= n || 1;
return Array(n+1).join(this);
}
You can declare like this
const [alice, bob] = [1, 2]
console.log(alice) // 1
console.log(bob) // 2

How to initialize an array's length in JavaScript?

Most of the tutorials that I've read on arrays in JavaScript (including w3schools and devguru) suggest that you can initialize an array with a certain length by passing an integer to the Array constructor using the var test = new Array(4); syntax.
After using this syntax liberally in my js files, I ran one of the files through jsLint, and it freaked out:
Error: Problem at line 1 character 22: Expected ')' and instead saw '4'.
var test = new Array(4);
Problem at line 1 character 23: Expected ';' and instead saw ')'.
var test = new Array(4);
Problem at line 1 character 23: Expected an identifier and instead saw ')'.
After reading through jsLint's explanation of its behavior, it looks like jsLint doesn't really like the new Array() syntax, and instead prefers [] when declaring arrays.
So I have a couple questions:
First, why? Am I running any risk by using the new Array() syntax instead? Are there browser incompatibilities that I should be aware of?
And second, if I switch to the square bracket syntax, is there any way to declare an array and set its length all on one line, or do I have to do something like this:
var test = [];
test.length = 4;
Array(5) gives you an array with length 5 but no values, hence you can't iterate over it.
Array.apply(null, Array(5)).map(function () {}) gives you an array with length 5 and undefined as values, now it can be iterated over.
Array.apply(null, Array(5)).map(function (x, i) { return i; }) gives you an array with length 5 and values 0,1,2,3,4.
Array(5).forEach(alert) does nothing, Array.apply(null, Array(5)).forEach(alert) gives you 5 alerts
ES6 gives us Array.from so now you can also use Array.from(Array(5)).forEach(alert)
If you want to initialize with a certain value, these are good to knows...
Array.from('abcde'), Array.from('x'.repeat(5))
or Array.from({length: 5}, (v, i) => i) // gives [0, 1, 2, 3, 4]
With ES2015 .fill() you can now simply do:
// `n` is the size you want to initialize your array
// `0` is what the array will be filled with (can be any other value)
Array(n).fill(0)
Which is a lot more concise than Array.apply(0, new Array(n)).map(i => value)
It is possible to drop the 0 in .fill() and run without arguments, which will fill the array with undefined. (However, this will fail in Typescript)
Why do you want to initialize the length? Theoretically there is no need for this. It can even result in confusing behavior, because all tests that use the length to find out whether an array is empty or not will report that the array is not empty.
Some tests show that setting the initial length of large arrays can be more efficient if the array is filled afterwards, but the performance gain (if any) seem to differ from browser to browser.
jsLint does not like new Array() because the constructer is ambiguous.
new Array(4);
creates an empty array of length 4. But
new Array('4');
creates an array containing the value '4'.
Regarding your comment: In JS you don't need to initialize the length of the array. It grows dynamically. You can just store the length in some variable, e.g.
var data = [];
var length = 5; // user defined length
for(var i = 0; i < length; i++) {
data.push(createSomeObject());
}
[...Array(6)].map(x => 0);
// [0, 0, 0, 0, 0, 0]
OR
Array(6).fill(0);
// [0, 0, 0, 0, 0, 0]
Note: you can't loop empty slots i.e. Array(4).forEach(() => …)
OR
( typescript safe )
Array(6).fill(null).map((_, i) => i);
// [0, 1, 2, 3, 4, 5]
OR
Classic method using a function ( works in any browser )
function NewArray(size) {
var x = [];
for (var i = 0; i < size; ++i) {
x[i] = i;
}
return x;
}
var a = NewArray(10);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Creating nested arrays
When creating a 2D array with the fill intuitively should create new instances. But what actually going to happen is the same array will be stored as a reference.
var a = Array(3).fill([6]);
// [ [6], [6], [6] ]
a[0].push(9);
// [ [6, 9], [6, 9], [6, 9] ]
Solution
var a = [...Array(3)].map(x => []);
a[0].push(4, 2);
// [ [4, 2], [], [] ]
So a 3x2 Array will look something like this:
[...Array(3)].map(x => Array(2).fill(0));
// [ [0, 0], [0, 0], [0, 0] ]
N-dimensional array
function NArray(...dimensions) {
var index = 0;
function NArrayRec(dims) {
var first = dims[0], next = dims.slice().splice(1);
if(dims.length > 1)
return Array(dims[0]).fill(null).map((x, i) => NArrayRec(next ));
return Array(dims[0]).fill(null).map((x, i) => (index++));
}
return NArrayRec(dimensions);
}
var arr = NArray(3, 2, 4);
// [ [ [ 0, 1, 2, 3 ] , [ 4, 5, 6, 7] ],
// [ [ 8, 9, 10, 11] , [ 12, 13, 14, 15] ],
// [ [ 16, 17, 18, 19] , [ 20, 21, 22, 23] ] ]
Initialize a chessboard
var Chessboard = [...Array(8)].map((x, j) => {
return Array(8).fill(null).map((y, i) => {
return `${String.fromCharCode(65 + i)}${8 - j}`;
});
});
// [ [A8, B8, C8, D8, E8, F8, G8, H8],
// [A7, B7, C7, D7, E7, F7, G7, H7],
// [A6, B6, C6, D6, E6, F6, G6, H6],
// [A5, B5, C5, D5, E5, F5, G5, H5],
// [A4, B4, C4, D4, E4, F4, G4, H4],
// [A3, B3, C3, D3, E3, F3, G3, H3],
// [A2, B2, C2, D2, E2, F2, G2, H2],
// [A1, B1, C1, D1, E1, F1, G1, H1] ]
Math filled values
handy little method overload when working with math
function NewArray( size , method, linear )
{
method = method || ( i => i );
linear = linear || false;
var x = [];
for( var i = 0; i < size; ++i )
x[ i ] = method( linear ? i / (size-1) : i );
return x;
}
NewArray( 4 );
// [ 0, 1, 2, 3 ]
NewArray( 4, Math.sin );
// [ 0, 0.841, 0.909, 0.141 ]
NewArray( 4, Math.sin, true );
// [ 0, 0.327, 0.618, 0.841 ]
var pow2 = ( x ) => x * x;
NewArray( 4, pow2 );
// [ 0, 1, 4, 9 ]
NewArray( 4, pow2, true );
// [ 0, 0.111, 0.444, 1 ]
The shortest:
let arr = [...Array(10)];
console.log(arr);
ES6 introduces Array.from which lets you create an Array from any "array-like" or iterables objects:
Array.from({length: 10}, (x, i) => i);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In this case {length: 10} represents the minimal definition of an "array-like" object: an empty object with just a length property defined.
Array.from allows for a second argument to map over the resulting array.
Sparse arrays are here! 🥳 [2021]
In modern JS engines, sparse arrays are fully supported. You can use [] or new Array(len) in any way you like, even with random access. Dictionary mode seems to be a thing of the past.
In current Chrome (and I guess any V8 environment), Arrays can have a length of up to 2^32-1 and allocation is sparse (meaning empty chunks don't use up any memory):
However, there is a catch
On the one hand, for loops work as intended, however, Array's builtin higher order functions (such as map, filter, find, some etc.) ignore unassigned elements. They require fill (or some other method of population) first:
const a = new Array(10);
const b = new Array(10).fill(0);
a.forEach(x => console.log(x)); // does nothing
b.forEach(x => console.log(x)); // works as intended
Old Version
(I removed most of the old version.) The gist was that creating a large array using new Array(largeNumber) or random accessing an array in places that have not yet been allocated would tumble it into "dictionary mode". Meaning you are using an array with indexes, but under the hood it would use a dictionary to store the values, thus messing with performance, and also with iteration behavior. Luckily that is a thing of the past.
This will initialize the length property to 4:
var x = [,,,,];
I'm surprised there hasn't been a functional solution suggested that allows you to set the length in one line. The following is based on UnderscoreJS:
var test = _.map(_.range(4), function () { return undefined; });
console.log(test.length);
For reasons mentioned above, I'd avoid doing this unless I wanted to initialize the array to a specific value. It's interesting to note there are other libraries that implement range including Lo-dash and Lazy, which may have different performance characteristics.
Here is another solution
var arr = Array.apply( null, { length: 4 } );
arr; // [undefined, undefined, undefined, undefined] (in Chrome)
arr.length; // 4
The first argument of apply() is a this object binding, which we don't care about here, so we set it to null.
Array.apply(..) is calling the Array(..) function and spreading out the { length: 3 } object value as its arguments.
Please people don't give up your old habits just yet.
There is a large difference in speed between allocating memory once then working with the entries in that array (as of old), and allocating it many times as an array grows (which is inevitably what the system does under the hood with other suggested methods).
None of this matters of course, until you want to do something cool with larger arrays. Then it does.
Seeing as there still seems to be no option in JS at the moment to set the initial capacity of an array, I use the following...
var newArrayWithSize = function(size) {
this.standard = this.standard||[];
for (var add = size-this.standard.length; add>0; add--) {
this.standard.push(undefined);// or whatever
}
return this.standard.slice(0,size);
}
There are tradeoffs involved:
This method takes as long as the others for the first call to the function, but very little time for later calls (unless asking for a bigger array).
The standard array does permanently reserve as much space as the largest array you have asked for.
But if it fits with what you're doing there can be a payoff.
Informal timing puts
for (var n=10000;n>0;n--) {var b = newArrayWithSize(10000);b[0]=0;}
at pretty speedy (about 50ms for the 10000 given that with n=1000000 it took about 5 seconds), and
for (var n=10000;n>0;n--) {
var b = [];for (var add=10000;add>0;add--) {
b.push(undefined);
}
}
at well over a minute (about 90 sec for the 10000 on the same chrome console, or about 2000 times slower).
That won't just be the allocation, but also the 10000 pushes, for loop, etc..
(this was probably better as a comment, but got too long)
So, after reading this I was curious if pre-allocating was actually faster, because in theory it should be. However, this blog gave some tips advising against it http://www.html5rocks.com/en/tutorials/speed/v8/.
So still being unsure, I put it to the test. And as it turns out it seems to in fact be slower.
var time = Date.now();
var temp = [];
for(var i=0;i<100000;i++){
temp[i]=i;
}
console.log(Date.now()-time);
var time = Date.now();
var temp2 = new Array(100000);
for(var i=0;i<100000;i++){
temp2[i] = i;
}
console.log(Date.now()-time);
This code yields the following after a few casual runs:
$ node main.js
9
16
$ node main.js
8
14
$ node main.js
7
20
$ node main.js
9
14
$ node main.js
9
19
var arr=[];
arr[5]=0;
alert("length="+arr.length); // gives 6
The simplest form is to use
Array.from({ length: 3 });
// gives you
[undefined, undefined, undefined]
Unlike Array(3) which will give you an array you can't iterate over. Array.from({ length }) gives you an array you can iterate easily.
Array.from({ length: 3 }).map((e, idx) => `hi ${idx}`);
// ['hi 1', 'hi 2', 'hi 3']
Assuming that Array's length is constant. In Javascript, This is what we do:
const intialArray = new Array(specify the value);
The array constructor has an ambiguous syntax, and JSLint just hurts your feelings after all.
Also, your example code is broken, the second var statement will raise a SyntaxError. You're setting the property length of the array test, so there's no need for another var.
As far as your options go, array.length is the only "clean" one. Question is, why do you need to set the size in the first place? Try to refactor your code to get rid of that dependency.
In addition to the answers of others, another clever way is to use Float32Array to create an array and iterate on it.
For this purpose, create an instance from Float32Array with your desired length like this:
new Float32Array(5)
This code returns an array-like that you can convert it to an array with Array.from():
Array.from(new Float32Array(5)) // [0, 0, 0, 0, 0]
You can also use fill() to change the value of items:
Array.from(new Float32Array(5).fill(2)) // [2, 2, 2, 2, 2]
And of course you can iterate on it:
Array.from(new Float32Array(5)).map(item => /* ... */ )
In most answers it is recommended to fill the array because otherwise "you can't iterate over it", but this is not true. You can iterate an empty array, just not with forEach. While loops, for of loops and for i loops work fine.
const count = Array(5);
Does not work.
console.log('---for each loop:---');
count.forEach((empty, index) => {
console.log(`counting ${index}`);
});
These work:
console.log('---for of loop:---');
for (let [index, empty] of count.entries()) {
console.log(`counting for of loop ${index}`);
}
console.log('---for i loop:---');
for (let i = 0, il = count.length; i < il; ++i) {
console.log(`counting for i loop ${i}`);
}
console.log('---while loop:---');
let index = 0;
while (index < count.length) {
console.log(`counting while loop ${index}`);
index++;
}
Check this fiddle with the above examples.
Also angulars *ngFor works fine with an empty array:
<li *ngFor="let empty of count; let i = index" [ngClass]="
<span>Counting with *ngFor {{i}}</span>
</li>
You can set the array length by using array.length = youValue
So it would be
var myArray = [];
myArray.length = yourValue;
The reason you shouldn't use new Array is demonstrated by this code:
var Array = function () {};
var x = new Array(4);
alert(x.length); // undefined...
Some other code could mess with the Array variable. I know it's a bit far fetched that anyone would write such code, but still...
Also, as Felix King said, the interface is a little inconsistent, and could lead to some very difficult-to-track-down bugs.
If you wanted an array with length = x, filled with undefined (as new Array(x) would do), you could do this:
var x = 4;
var myArray = [];
myArray[x - 1] = undefined;
alert(myArray.length); // 4

Categories

Resources