This is my code, acting upon myArray:
var myArray = [];
var i;
for(i = 0; i < 20; i += 1) {
myArray.push(Math.random());
}
Is there a functional equivalent of the above that does without the dummy variable i?
Favorite answers:
while(myArray.push(Math.random()) < 20);
$.map(Array(20), Math.random);
for(var myArray = []; myArray.push(Math.random()) < 20;);
Not in ES5, there's no real functional equivalent to it, as you have to have something which has an amount of 20 to apply map to...
var my20ElementArray = [0,1,2,3,4,5,6,7,8,9,10];
var myArray = my20ElementArray.map(Math.random);
You could create an xrange-like function what is in Python but that would just hide this "unused" variable inside a function.
With JavaScript 1.7, you can use Array comprehensions for this task:
var myArray = [Math.random() for each (i in range(0, 20))];
However, with ES5.1 you can just use the Array constructor to generate an array of arbitrary length, and then map it to random numbers. Only drawback is that map() does not work with uninitialised values, so I first generate an Array of empty strings by using join and split:
var myArray = new Array(20).join(" ").split(" ").map(Math.random);
Ugly, but short. A maybe better (but less understandable) idea from Creating range in JavaScript - strange syntax:
var myArray = Array.apply(null, {length: 20}).map(Math.random);
Starting with #FelixKlings comment, one could also use this one-liner without the i loop variable:
for (var myArray=[]; myArray.push(Math.random()) < 20;);
// much better:
for (var myArray=[]; myArray.length < 20;) myArray.push(Math.random());
Are you looking for something as follows:
function makeArray(length, def) {
var array = [];
var funct = typeof def === "function";
while (array.push(funct ? def() : def) < length);
return array;
}
Then you can create arrays as follows:
var array = makeArray(100); // an array of 100 elements
var zero = makeArray(5, 0); // an array of 5 `0`s
In your case you may do something like:
var myArray = makeArray(20, Math.random);
See the following fiddle: http://jsfiddle.net/WxtkF/3/
how about this?
it's functionale style and it's very concise.
var makeRandomArray = function(n){
if (n == 0) return [];
return [Math.random()].concat(makeRandomArray(n-1));
};
console.log(makeRandomArray(20))
http://jsfiddle.net/YQqGP/
You could try:
var myArray = String(Array(20)).split(',')
.map( () => Math.random() );
Or extend the Array prototype with something like:
Array.prototype.vector = function(n,fn){
fn = fn || function(){return '0';};
while (n--){
this.push(fn());
}
return this;
}
// usage
var myArray = [].vector(20, () => Math.random());
Or try something funny:
var myArray = function a(n,fn){
return n ? a(n-1,fn).concat(fn()) : [];
}(20, () => Math.random())
Or use Array.from (ES>=2015)
Array.from({length: 20}).map(() => Math.random())
Related
I was toying a bit and was trying to instantiate a new array of length x, where all elements of that array were initialized to a value y:
var arr = new Array(x).fill(y);
This works well if the value of y is anything other than an object.
Meaning that if y is an object, the following is true:
var arr = new Array(2).fill({});
arr[0] === arr[1]; //is true;
arr[0].test = 'string';
arr[1].test === 'string'; //is also true;
Is there any way to state that a new object should be created for each element while using the fill-function? Or should I just convert it to a loop?
You can first fill the array with any value (e.g. undefined), and then you will be able to use map:
var arr = new Array(2).fill().map(u => ({}));
var arr = new Array(2).fill().map(Object);
The accepted answer is good and would work in 90% of cases.
But if you are making high-performance JS application, and if you work with big/huge arrays, Array.map(..) creates big overload in both - memory and processor use, as it creates a copy of an array.
I recommend to use the classic for loop:
a = new Array(ARRAY_SIZE);
for (var i = 0; i < ARRAY_SIZE; i++) {
a[i] = [];
}
// or it's one line alternative
for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
I tested six alternatives and got this:
Array.map(), as proposed above (11x times!!! slower):
a = new Array(ARRAY_SIZE).fill().map(u => { return []; });
for loop, the best one (fastest):
// Standard multi-line way
a = new Array(ARRAY_SIZE);
for (var i = 0; i < ARRAY_SIZE; i++) {
a[i] = [];
}
// One line syntax
for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
forEach (6x time slower):
a = new Array(ARRAY_SIZE).fill();
a.forEach((val, i) => {
a[i] = [];
})
[UPDATE 2020-08-27] One more way proposed by Ilias Karim below
Array.from (30x times!!! slower) - apparently worse in terms of performance, despite the nicest syntax :(
a = Array.from({ length: ARRAY_SIZE }, () => []);
[..Array(..)] (5x times!!! slower)
a = [...Array(ARRAY_SIZE)].map(_=>([]))
Array.push(..), second place in terms of performance (2x times!!! slower)
let a = [], total = ARRAY_SIZE;
while(total--) a.push([]);
PS. I used this fiddle for tests.
One performant solution:
Array.from({ length: 5 }, () => new Object())
Shortest Possable:
let node = [...Array(2)].map(_=>({}))
console.log(node)
Ilias Karim's answer is most excellent. I just did the following:
a = Array.from({length:l}, () => new Array(c).fill(prefix));
to create a pre-filled 2D array of the specified size, l by c, filled with prefix. Now my code can fill in the slots in the 2D matrix that need non-prefix values.
I wrote a blog post about this: http://www.samhenderson.xyz/posts/12
But the TLDR is that if you want to avoid chaining multiple function e.g. fill, map. And want to avoid writing a loop, then you can use:
const array = Array.from({ length: 2 },()=>({}))
For an array of arrays:
const array = Array.from({ length: 2 },()=>([]))
To add to answers that explain the aliasing issue and how to solve it, here's a handy function that can be used to create arrays with cleaner syntax for the caller:
const array = (length, fill) =>
[...Array(length)].map((_, i) =>
typeof fill === "function" ? fill(i) : fill
);
// usage:
const a = array(3, i => array(3, j => [i, j]));
a[0][0][0] = -42;
console.log(a);
Note that you still need to use a callback function for non-primitive values. This is actually a feature as it exposes the index and lets you provide arbitrary logic to fill the element. If you're concerned about accidentally passing a non-primitive, non-function object as the fill value, you can throw an error.
If you really want to be able to pass an object directly and have it copied under the hood, here's an adjustment that pretty much prohibits aliasing:
const array = (length, fill) =>
[...Array(length)].map((x, i) =>
typeof fill === "function" ? fill(i) :
typeof fill === "object" ? _.cloneDeep(fill) : fill
);
// usage:
const a = array(2, array(2, {foo: 3}));
a[0][0].foo = 42;
console.log(a);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
In general, I suggest avoiding .fill() almost entirely in favor of the spread syntax [...Array()] because it's easy to forget about the aliasing behavior and wind up with a frustrating bug.
If speed matters, use a traditional for loop:
const array = (length, fill) => {
const a = [];
for (let i = 0; i < length; i++) {
a[i] = typeof fill === "function" ? fill(i) : fill;
}
return a;
};
// usage:
const a = array(2, () => array(2, Object));
a[0][0].foo = 42;
console.log(a);
I was toying a bit and was trying to instantiate a new array of length x, where all elements of that array were initialized to a value y:
var arr = new Array(x).fill(y);
This works well if the value of y is anything other than an object.
Meaning that if y is an object, the following is true:
var arr = new Array(2).fill({});
arr[0] === arr[1]; //is true;
arr[0].test = 'string';
arr[1].test === 'string'; //is also true;
Is there any way to state that a new object should be created for each element while using the fill-function? Or should I just convert it to a loop?
You can first fill the array with any value (e.g. undefined), and then you will be able to use map:
var arr = new Array(2).fill().map(u => ({}));
var arr = new Array(2).fill().map(Object);
The accepted answer is good and would work in 90% of cases.
But if you are making high-performance JS application, and if you work with big/huge arrays, Array.map(..) creates big overload in both - memory and processor use, as it creates a copy of an array.
I recommend to use the classic for loop:
a = new Array(ARRAY_SIZE);
for (var i = 0; i < ARRAY_SIZE; i++) {
a[i] = [];
}
// or it's one line alternative
for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
I tested six alternatives and got this:
Array.map(), as proposed above (11x times!!! slower):
a = new Array(ARRAY_SIZE).fill().map(u => { return []; });
for loop, the best one (fastest):
// Standard multi-line way
a = new Array(ARRAY_SIZE);
for (var i = 0; i < ARRAY_SIZE; i++) {
a[i] = [];
}
// One line syntax
for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
forEach (6x time slower):
a = new Array(ARRAY_SIZE).fill();
a.forEach((val, i) => {
a[i] = [];
})
[UPDATE 2020-08-27] One more way proposed by Ilias Karim below
Array.from (30x times!!! slower) - apparently worse in terms of performance, despite the nicest syntax :(
a = Array.from({ length: ARRAY_SIZE }, () => []);
[..Array(..)] (5x times!!! slower)
a = [...Array(ARRAY_SIZE)].map(_=>([]))
Array.push(..), second place in terms of performance (2x times!!! slower)
let a = [], total = ARRAY_SIZE;
while(total--) a.push([]);
PS. I used this fiddle for tests.
One performant solution:
Array.from({ length: 5 }, () => new Object())
Shortest Possable:
let node = [...Array(2)].map(_=>({}))
console.log(node)
Ilias Karim's answer is most excellent. I just did the following:
a = Array.from({length:l}, () => new Array(c).fill(prefix));
to create a pre-filled 2D array of the specified size, l by c, filled with prefix. Now my code can fill in the slots in the 2D matrix that need non-prefix values.
I wrote a blog post about this: http://www.samhenderson.xyz/posts/12
But the TLDR is that if you want to avoid chaining multiple function e.g. fill, map. And want to avoid writing a loop, then you can use:
const array = Array.from({ length: 2 },()=>({}))
For an array of arrays:
const array = Array.from({ length: 2 },()=>([]))
To add to answers that explain the aliasing issue and how to solve it, here's a handy function that can be used to create arrays with cleaner syntax for the caller:
const array = (length, fill) =>
[...Array(length)].map((_, i) =>
typeof fill === "function" ? fill(i) : fill
);
// usage:
const a = array(3, i => array(3, j => [i, j]));
a[0][0][0] = -42;
console.log(a);
Note that you still need to use a callback function for non-primitive values. This is actually a feature as it exposes the index and lets you provide arbitrary logic to fill the element. If you're concerned about accidentally passing a non-primitive, non-function object as the fill value, you can throw an error.
If you really want to be able to pass an object directly and have it copied under the hood, here's an adjustment that pretty much prohibits aliasing:
const array = (length, fill) =>
[...Array(length)].map((x, i) =>
typeof fill === "function" ? fill(i) :
typeof fill === "object" ? _.cloneDeep(fill) : fill
);
// usage:
const a = array(2, array(2, {foo: 3}));
a[0][0].foo = 42;
console.log(a);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
In general, I suggest avoiding .fill() almost entirely in favor of the spread syntax [...Array()] because it's easy to forget about the aliasing behavior and wind up with a frustrating bug.
If speed matters, use a traditional for loop:
const array = (length, fill) => {
const a = [];
for (let i = 0; i < length; i++) {
a[i] = typeof fill === "function" ? fill(i) : fill;
}
return a;
};
// usage:
const a = array(2, () => array(2, Object));
a[0][0].foo = 42;
console.log(a);
Case: We have 'n' number of arrays stored in an array (Array of Arrays). Now that each child array in this parent array can have elements that may or may not be present in other child arrays. Output - I need to create an array which has the all the elements present in all the child arrays excluding the duplicates.
I do not want to concatenate all the arrays into a single array and use unique method to filter out. I need to create unique array then and there during iteration.
Ex:
var a[] = [1,2,3,4,5];
var b[] = [1,2,7,8];
var c[] = [1,2,3,4,5,6,7,8];
var d[] = [9,10,11,12];
var arr[] = [a,b,c,d]
Output must be [1,2,3,4,5,6,7,8,9,10,11,12]
P.S: I can concat the arrays and use jquery unique function to resolve this, but i need a solution in javascript alone. Thanks
You can use array#reduce to flatten your array and then use Set to get distinct values and use array#from to get back array from Set.
var a = [1,2,3,4,5];
var b = [1,2,7,8];
var c = [1,2,3,4,5,6,7,8];
var d = [9,10,11,12];
var arr = [a,b,c,d]
var result = Array.from(new Set(arr.reduce((r,a) => r.concat(a))));
console.log(result);
Try using .filter when adding each array to the final one, filtering out the duplicates:
a.filter(function(item) {
return !finalArray.contains(item));
});
Answer using Sets:
var a = [1,2,3,4,5];
var b = [1,2,7,8];
var c = [1,2,3,4,5,6,7,8];
var d = [9,10,11,12];
var concat = a.concat(b).concat(c).concat(d);
var union = new Set(concat);
//console.log(union);
ES6 Answer:
let a = new Set([1,2,3,4,5]);
let b = new Set([1,2,7,8]);
let c = new Set([1,2,3,4,5,6,7,8]);
let d = new Set([9,10,11,12]);
let arr = new Set([...a,...b,...c,...d]);
//Result in arr.
Whats going on???
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set:
The Set object lets you store unique values of any type, whether
primitive values or object references.
So when we initialise Sets passing arrays to the constructor we basically ensure that there are no duplicate values.
Then in the last line, we concat all the Sets we initialised prior into a final set.
The ... notation converts the Set into an array, and when we pass the 4 arrays to the constructor of the Set they get concatenated and a Set of their unique values is created.
Here is a functional alternative written in ES5.
var flatten = function(list) {
return list.reduce(function(acc, next) {
return acc.concat(Array.isArray(next) ? flatten(next) : next);
}, []);
};
var unique = function(list) {
return list.filter(function(element, index) {
return list.indexOf(element) === index;
})
}
var a = [1,2,3,4,5];
var b = [1,2,7,8];
var c = [1,2,3,4,5,6,7,8];
var d = [9,10,11,12];
var arr = [a,b,c,d];
var result = unique(flatten(arr));
console.log(result);
If you support ES6, arrow function can make that code even shorter.
Here is a solution that uses a plain object for resolving duplicates, and only uses basic ES3 JavaScript. Runs in IE 5.5 and higher, and with O(n) time complexity.
function uniques(arr) {
var obj = {}, result = [];
for (var i = 0; i < arr.length; i++) {
obj[arr[i]] = true;
}
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) result.push(+prop);
}
return result;
}
// Example use
var a = [1,2,3,4,5],
b = [1,2,7,8],
c = [1,2,3,4,5,6,7,8],
d = [9,10,11,12];
var result = uniques(a.concat(b, c, d));
console.log('Result: ' + result);
As an object can only have a unique set of properties (no duplicates), the use of all array values as properties in an object will give you an object with a property for each unique value. This happens in the first loop. NB: the value given to those properties is not relevant; I have used true.
Then the result is just the conversion of those properties back to array values. This happens in the second loop.
var a = [1,2,3,4,5];
var b = [1,2,7,8];
var c = [1,2,3,4,5,6,7,8];
var d = [9,10,11,12];
var result = a.concat(b,c,d);
function remvDup(result){
var tmp = [];
for(var i = 0; i < result.length; i++){
if(tmp.indexOf(result[i]) == -1){
tmp.push(result[i]);
}
}
return tmp;
}
console.log(remvDup(result));
Becuase the OP mentioned that he cannot use 'Set' as it is not supported on the targeted browsers, I would recommand using the 'union' function from the lodash library.
See union's documentation here
I have a question of JS arrays.
Example:
var fullArr = [1,2,3,4];
var partArr = [2,3];
var newArr = [];
We have a main array fullArr and a partial array partarr. I want to create a function/filter, which is looking for existing items in fullArr and not in partArr.
In this example above newArr must be equal to [1,4].
I've tried doing something like this, but it's not working properly.
for (var k in fullArray) { // [1,2,3,4]
for (var j in selectedArray) { // [1,4]
if (fullArray[k] == selectedArray[j]) {
newArray.splice(selectedArray[j] - 1, 1); // must be [2,3]
break;
}
}
}
What is a good way of making this? Thanks.
Here's one
var newArr = fullArr.filter(function(f) { // The filter() method creates a new array with all elements that pass the test implemented by the provided function.
return partArr.indexOf(f) == -1; // The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present.
})
to impress the girls, you can also
var newArr = fullArr.filter(function(f) {
return !~partArr.indexOf(f);
})
Here is the code for your requirement.
var fullArr = [1,2,3,4];
var partArr = [2,3];
var newArr = [];
for(var i=0;i<fullArr.length;i++){
if(partArr.indexOf(fullArr[i]) == -1)
newArr.push(fullArr[i]);
};
Here is the working Link
Hope it works :)
In fact, you want a common part between arrays. Obviously you can choose splice or indexOf to have O(n * m) or even O(m * n^2) performance. It's obviously suboptimal for any array larger than few elements
Or you can use objects as hash maps to find differences in (in worst case) O(n + m log m):
var fullArr = [1,2,3,4];
var partArr = [2,3];
var temporaryObject = Object.create(null);
partArr.forEach(el=>temporaryObject[el] = true); // temporaryObject after this operation is {"2": true, "3": true}
var newArr = fullArr.filter(el=>temporaryObject[el]);
In this example I have used ES6 feature called "arrow functions". It translates to following ES5 code:
var partArr = [2, 3];
var temporaryObject = Object.create(null);
partArr.forEach(function (el) {
temporaryObject[el] = true;
}); // temporaryObject after this operation is {"2": true, "3": true}
var newArr = fullArr.filter(function (el) {
return temporaryObject[el];
});
You can use the filter() function that works on arrays:
var newArr = fullArr.filter(function(val, i, arr) {
return partArr.indexOf(val) === -1;
});
This will return a new array containing the values of every iteration that returns true.
Should you ever need to do this on an object in the future a great way is to first convert the object keys to an array and then run the filter:
Object.keys(myObj).function(val, i, arr) {
return partArr.indexOf(val) === -1;
});
Here are few other approaches:
var fullArr = [1,2,3,4];
var partArr = [2,3];
var newArr = [];
1.
fullArr.map(function(element){
if(partArr.indexOf(element) === -1) newArr.push(element);
})
console.log(newArr);
2.
for(i in fullArr){
if(partArr.indexOf(fullArr[i]) === -1) newArr.push(fullArr[i]);
}
console.log(newArr);
3.
fullArr.forEach(function(element){
if(partArr.indexOf(element) === -1) newArr.push(element);
})
console.log(newArr);
I've been trying to find a reasonably concise way to set the dimensions of an empty multidimensional JavaScript array, but with no success so far.
First, I tried to initialize an empty 10x10x10 array using var theArray = new Array(10, 10 10), but instead, it only created a 1-dimensional array with 3 elements.
I've figured out how to initialize an empty 10x10x10 array using nested for-loops, but it's extremely tedious to write the array initializer this way. Initializing multidimensional arrays using nested for-loops can be quite tedious: is there a more concise way to set the dimensions of empty multidimensional arrays in JavaScript (with arbitrarily many dimensions)?
//Initializing an empty 10x10x10 array:
var theArray = new Array();
for(var a = 0; a < 10; a++){
theArray[a] = new Array();
for(var b = 0; b < 10; b++){
theArray[a][b] = new Array();
for(var c = 0; c < 10; c++){
theArray[a][b][c] = 10
}
}
}
console.log(JSON.stringify(theArray));
Adapted from this answer:
function createArray(length) {
var arr = new Array(length || 0),
i = length;
if (arguments.length > 1) {
var args = Array.prototype.slice.call(arguments, 1);
while(i--) arr[i] = createArray.apply(this, args);
}
return arr;
}
Simply call with an argument for the length of each dimension.
Usage examples:
var multiArray = createArray(10,10,10); Gives a 3-dimensional array of equal length.
var weirdArray = createArray(34,6,42,2); Gives a 4-dimensional array of unequal lengths.
function multiDimArrayInit(dimensions, leafValue) {
if (!dimensions.length) {
return leafValue;
}
var arr = [];
var subDimensions = dimensions.slice(1);
for (var i = 0; i < dimensions[0]; i++) {
arr.push(multiDimArrayInit(subDimensions, leafValue));
}
return arr;
}
console.log(multiDimArrayInit([2,8], "hi")); // counting the nested "hi"'s yields 16 of them
demo http://jsfiddle.net/WPrs3/
Here is my take on the problem: nArray utility function
function nArray() {
var arr = new Array();
var args = Array.prototype.slice.call(arguments, 1);
for(var i=0;i<arguments[0];i++) {
arr[i] = (arguments.length > 1 && nArray.apply(this, args)) || undefined;
}
return arr;
}
Usage example:
var arr = nArray(3, 3, 3);
Results in 3x3x3 array of undefined values.
Running code with some tests also available as a Fiddle here: http://jsfiddle.net/EqT3r/7/
The more dimension you have, the more you have interest in using one single flat array and a getter /setter function for your array.
Because for a [d1 X d2 X d3 X .. X dn] you'll be creating d2*d3*...*dn arrays instead of one, and when accessing, you'll make n indirection instead of 1.
The interface would look like :
var myNArray = new NArray(10,20,10);
var oneValue = myNArray.get(5,8,3);
myNArray.set(8,3,2, 'the value of (8,3,2)');
the implementation depends on your preference for a fixed-size
n-dimensionnal array or an array able to push/pop and the like.
A more succinct version of #chris code:
function multiDim (dims, leaf) {
dims = Array.isArray (dims) ? dims.slice () : [dims];
return Array.apply (null, Array (dims.shift ())).map (function (v, i) {
return dims.length
? multiDim (dims, typeof leaf == 'string' ? leaf.replace ('%i', i + ' %i') : leaf)
: typeof leaf == 'string' ? leaf.replace ('%i', i) : leaf;
});
}
console.log (JSON.stringify (multiDim ([2,2], "hi %i"), null, ' '));
Produces :
[
[
"hi 0 0",
"hi 0 1"
],
[
"hi 1 0",
"hi 1 1"
]
]
In this version you can pass the first argument as a number for single dimension array.
Including %i in the leaf value will provide index values in the leaf values.
Play with it at : http://jsfiddle.net/jstoolsmith/r3eMR/
Very simple function, generate an array with any number of dimensions. Specify length of each dimension and the content which for me is '' usually
function arrayGen(content,dims,dim1Len,dim2Len,dim3Len...) {
var args = arguments;
function loop(dim) {
var array = [];
for (var a = 0; a < args[dim + 1]; a++) {
if (dims > dim) {
array[a] = loop(dim + 1);
} else if (dims == dim) {
array[a] = content;
}
}
return array;
}
var thisArray = loop(1);
return thisArray;
};
I use this function very often, it saves a lot of time