Assigning values whilst creating a 2D array - javascript

Here is some Javascript code that creates a 2-dimension array and fills each cell with a random number.
// populate 2D array with integers
const row = 5, col = 7
let pic = new Array(row).fill(0).map(item => (new Array(col).fill(0)))
for (let x = 0; x < row; x++) {
for (let y = 0; y < col; y++) {
pic[x][y] = Math.floor((Math.random() * 90) + 10)
}
}
console.log(JSON.stringify(pic))
I'm looking for a more 'elegant' solution. My questions:
Is there a way to use the fill so that I can put in my target values? Then I can be finished with creating the array in one line.
How do I use a double .map to populate the 2D array, instead of a double for loop?
Is there a way to assign the output from the map / for loops directly into a variable? Then I don't need a separate create statement.
What is the best way to reshape an array? For example, changing a 1-by-10 array into a 5-by-2 array.
Is there a way to enforce a type? For instance the first dimension is a string, 2nd is an integer, etc.
Feel free to add your own definition of elegance. One of the things I'm looking for is a flexible approach that can also work with 3D arrays.

You could take a nested Array.from with a length and a mapping function.
const
fn = () => Math.floor(Math.random() * 5),
row = 5,
col = 7,
array = Array.from({ length: row }, () => Array.from({ length: col }, fn));
array.forEach(a => console.log(...a));

Is there a way to use the fill so that I can put in my target values? Then I can be finished with creating the array in one line.
No, fill is not that flexible. There is Array.from(iterable, callback) but I find it cumbersome and it is slow. I'd rather write that utility quickly
function array(n, callback){
const output = Array(n);
for(let i=0; i<n; ++i) output[i] = callback(i);
return output;
}
How do I use a double .map to populate the 2D array, instead of a double for loop?
map creates a new Array, by calling the callback function for each item on the current Array. You can abuse it to mutate the Array that is iterating. You can ignore the returnes Array and abuse it as forEach; but then map simply is the wrong tool.
var newMatrix = Array(5).fill().map(() => Array(7).fill().map(() => Math.random()));
the fill part is necessary, because Array(length) creates a sparse Array of that length and map only iterated defined indices (even if they contain undefined)
Is there a way to assign the output from the map / for loops directly into a variable? Then I don't need a separate create statement.
I'm not sure what you mean, because you already do that here let pic = new Array(row).fill(0).map(...)
What is the best way to reshape an array? For example, changing a 1-by-10 array into a 5-by-2 array.
function array(n, callback) {
const output = Array(n);
for (let i = 0; i < n; ++i) output[i] = callback(i);
return output;
}
function toGroupsOf(n, data) {
return array(Math.ceil(data.length / n), i => data.slice(n * i, n * (i + 1)));
}
const oneByTen = [array(10, v => v)];
console.log(oneByTen);
const twoByFive = toGroupsOf(5, oneByTen.slice().flat());
console.log(twoByFive);
Is there a way to enforce a type? For instance the first dimension is a string, 2nd is an integer, etc.
No, not in JS. btw. everything but the last dimension will be Arrays, not String.
But check out Typescript.
Feel free to add your own definition of elegance. One of the things I'm looking for is a flexible approach that can also work with 3D arrays.
// a general purpose function to create n-dimensional arrays.
// m(...dimensions, (...indices) => value)
function m(...args) {
return args.reduceRight((cb, length) => (...indices) => {
const output = Array(length);
for (let i = 0; i < length; ++i)
output[i] = cb(...indices, i);
return output;
})();
}
let data = m(5,7, () => Math.floor(Math.random() * 90 + 10));
console.log(data);
// 4-dimensions
console.log(m(2,3,4,5, Math.random));
// a 5x5 identity-matrix
console.log(m(5,5, (i,j) => i === j? 1: 0).join("\n"));
I'm a user of strongly typed languages like Scala, where for instance, you could never store a string in an integer variable. I find the laissez faire of Javascript difficult.
I have mixed opinions on that. I loved the way that static types and compile-time errors found little mistakes/oversights back when I learned (in AS3). Nowadays and with Typescript I often find Typescript to be too opinionated and find myself thinking f off compiler, I know/mean what I'm writing here and prefer the flexibility of JS. On the other hand, I still enjoy the assistance that comes from the IDE knowing what Objects I'm currently dealing with and what properties/methods they provide.

Heavily inspired by: https://stackoverflow.com/a/53859978/9758920
const row = 5, col = 7;
let pic = [...Array(row)].map(r => [...Array(col)].map(c => ~~(Math.random()*90)+10));
console.log(pic)

This should work.
const randomNumber = Math.floor((Math.random()*90)+10);
const randomMatrix = (row, col) => {
return new Array(row).fill(randomNumber).map(item => (new Array(col).fill(randomNumber)))
}
console.log(randomMatrix(5, 7))

Try the snippet below. initializeArray accepts parameters for width, height and a value for each cell.
const initialize2DArray = (w, h, val = null) =>
Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val));
console.log(initialize2DArray(3, 3, 0)) // 3x3 matrix filled with zeros
If you prefer a N-dimension array, try the snippet below:
const initializeNDArray = (val, ...args) =>
args.length === 0
? val
: Array.from({ length: args[0] }).map(() => initializeNDArray(val, ...args.slice(1)));
console.log(initializeNDArray(-1, 3, 3, 3))

Related

One line Array iterator javascript

I am coming from Python and I am looking for a single line iterator function for any array, where I can also check a condition and return a simple change to the items.
Expected result should match the method:
function arrayEvenItemIncrement(myArray){
for (let i = 0; i < myArray.length; i++){
if (myArray[i]%2==0){
myArray[i]++;
}
}
return myArray;
}
I tried using for (i of myArray){ } but this still doesn't serve my purpose.
I think the clearest way to do what you want here would be to map to a new array instead of mutating the old one:
const arrayEvenItemIncrement = myArray =>
myArray.map((val, i) => i % 2 === 1 ? val : val + 1);
If you have to mutate the existing array, it gets significantly uglier.
const arrayEvenItemIncrement = myArray => (
myArray.forEach((val, i) => { if (i % 2 === 0) myArray[i]++; }), myArray);
or
const arrayEvenItemIncrement = myArray => {
myArray.forEach((val, i) => { if (i % 2 === 0) myArray[i]++; }); return myArray };
But I wouldn't recommend that - put it on multiple lines instead.
You can technically squeeze any JS code into a single line, but past simple manipulations, it usually isn't a good idea because it sacrifices readability, which is far more important than reducing LoC. Professional, maintainable code is not a golfing competition.
Thank you for the answers.
I have settled for this:
myArray.forEach(el=>el%2===0?newArray.push(el):null);

Simplest vanilla JavaScript way to pair items from a unidimensional array into a multidimensional array?

Currently I have a unidimensional array, e.g. ['thing1', 'cond1', 'thing2', 'cond2', 'thing3']
I would like to pair each item to create a new multidimensional array like so [['thing1', 'cond1'], ['thing2', 'cond2'], ['thing3']]. I don't mind the last item being ['thing3', undefined] – if anything this is preferable unless someone raises this as bad practice.
So far I have
const pair = (arr) => {
let paired = [];
for (i = 0; i < arr.length; i += 2) {
paired.push([arr[i], arr[i+1]]);
}
return paired;
}
You can try this out in my JS Bin example.
This works perfectly fine AFAIA but I'd love this to be as concise as possible using modern JS and I'm not as polished as I should be with my array manipulation.
Thanks in advance to everyone who gives this a go.
Let the challenge... BEGIN!
You could take a while loop with an index variable. For pushing a pair take slice.
const pair = array => {
let paired = [],
i = 0;
while (i < array.length) paired.push(array.slice(i, i += 2));
return paired;
}
var array = ['thing1', 'cond1', 'thing2', 'cond2', 'thing3'],
paired = pair(array);
console.log(paired);
You can try this regex. Group the values by finding the number by using regular expression.
const data = ['thing1', 'cond1', 'thing2', 'cond2', 'thing3'];
const result = {};
data.forEach(value => {
const index = value.replace(/[^\d.]/g, '');
if (typeof result[index] == 'undefined') {
result[index] = [];
}
result[index].push(value);
});
const array = Object.values(result);
console.log(array);

JavaScript array views

Via XMLHttpRequest I have received arraybuffer of Uint32 values
oReq.onload = function (oEvent) {
var arrayBuffer = oReq.response;
if (arrayBuffer) {
pointsArray = new Uint32Array(arrayBuffer);
However, I know that this array has an internal structure.
Say, pointsArray length is 10 but I know it contains 5 points X,Y coordinates.
How can I create( hopefully without copy ) two new 'views' at this pointsArray so that I can
index X and Y points separately?
Something like:
var xArray = something (pointsArray)
var yArray = something else (pointsArray)
Then, even if pointsArray length is 10, my new two arrays will have a length of 5 so I can index them from 0 to 4.
EDIT:
The question is imprecise. It implies that original input array can't be modified so the answer by David Alvarez is correct, despite the stated preference for avoiding any copies, which, in turn, for the best performance may require the format of the input array to be modified.
If there are 5 consecutive X coordinates and then 5 consecutive Y coordinates, you can create arrays on the same buffer:
let xArray = new Uint32Array(arrayBuffer,0,5);
let yArray = new Uint32Array(arrayBuffer,5*4,5);
(where 4 could be Uint32Array.BYTES_PER_ELEMENT)
But otherwise you will have to copy elements around, at least inside the array.
Side note: TypedArrays use platform-native byte order, so generally you can not avoid dealing with all data elements, at least in a conditional branch swapping bytes if necessary, or you can use DataView.getUint32(bytOffset,littleEndian) and you are back on the starting field, accessing elements individually.
What you can do is create functions.
const xArray = () => pointsArray.filter((element, index) => index % 2 === 0)
const yArray = () => pointsArray.filter((element, index) => index % 2 !== 0)
In that manner, no values will be copied or processed until you call xArray() or yArray(). That is what I think is the closest to a "view".
EDIT
If you want to use that "view" to directly access nth element:
const nthX = (n) => pointsArray.filter((element, index) => index % 2 === 0)[n]
const nthY = (n) => pointsArray.filter((element, index) => index % 2 !== 0)[n]
Then call nthX(2) if you want the x at the index 2 (of the array contianing all the x's)
EDIT 2
If you want the same behavior without copying the array you can do:
const nthX = (n) => pointsArray[n*2]
const nthY = (n) => pointsArray[n*2+1]

JavaScript : Pass-by-reference issues when creating an unknown dimension multi dimensional array function

I'm posting this question because I am trying to make a function that allows someone to create a multi-dim array. So, the user inputs an array of numbers which are the dimensions of the array (e.g entering [2, 4, 3] would output a 2x4x3 multi-dim array)
I have spent a couple of hours trying to imagine an algorithm that can do this in JS and I came up with this:
Note: I use Node.js v9.11.1
function generate(dimensions) {
// SA = sub-array (I will use this several times here)
// This array will store every SAs of the multi-dim array
// E.g for a 2x4x3 array, it will store a 2-item array, a 4-item array and a 3-item array
var arrays = []
// This fills `arrays` with the SAs
for (var i = 0; i < dimensions.length; i++) arrays.push(new Array(dimensions[i]).slice(0))
// Here it gets a bit complex (at least for me!)
// So what we do is that for each SA (except last), we fill it with copies of the current+1 SA
// So the SA at index 1 will be filled with copies of the array at index 2
// And the array at index 0 will be filled with arrays of index 1 (which was already filled because our for loop starts from the end)
// The array at index 0 is our final multi-dim array
// Goes from the before last SA to the first
for (var current = dimensions.length-2; current !== -1; current--) {
// Fills the current SA with index+1 SA
for (var i = 0; i < arrays[current].length; i++) arrays[current][i] = arrays[current+1].slice(0)
}
// Returns first array, the complete one
return arrays[0].slice(0)
}
My problem is that even if the array is well generated, some SA are passed by reference and not by value so when I do
my_array = generate([2, 4, 3])
my_array[1][2][1] = "hi!" // Fill a random place with "hi!"
Then when I do console.log(my_array), some other cases of the multi-dim array are filled with the same value.
This means that somewhere, an array is passed by reference rather than passed by value which is strange
because I checked the code multiple times and I don't find where this could come from (I use the Array.slice()
method to "copy" the array)
Have I missed something huge?
Your help would be rather appreciated!
To be honest, not sure how your trying to create your mult-dim array,..
But the first thing that springs to mind when seeing something like this, is recursion.
eg..
function generate(dimensions) {
if (!dimensions.length) throw new Error("no dims?");
const dimsize = dimensions[0];
if (dimensions.length === 1) {
return new Array(dimsize).fill(null);
}
const ret = [];
const subdims = dimensions.slice(1);
for (let l = 0; l < dimsize; l+= 1)
ret.push(generate(subdims));
return ret;
}
const my_array = generate([2, 4, 3])
my_array[1][2][1] = "hi!"
console.log(my_array);
I come up with this:
function generate(dims) {
if(dims.length > 0) {
let array = new Array(dims[0]).fill(0);
let childDims = dims.slice();
childDims.shift();
return array.map((el) => {
return generate(childDims);
});
} else return 0;
}
let foo = generate([2, 3, 2]);
foo[0][0][1] = 'hmmmm';
console.log(foo);
Also using recursion to create multidimensional array. But when creating arrays as You saw, have to be carefull about not passing references but real copies of arrays. Slice() will give You only shallow copy.

Split array into two different arrays using functional JavaScript

I was wondering what would be the best way to split an array into two different arrays using JavaScript, but to keep it in the realms of functional programming.
Let's say that the two arrays should be created depending on some logic. For instance splitting one array should only contain strings with less than four characters and the other the rest.
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
I have thought about different methods:
Filter:
const lessThanFour = arr.filter((animal) => {
return animal.length < 4;
});
const fourAndMore = arr.filter((animal) => {
return animal.length >= 4;
});
The problem with this for me is that you have to go through your data twice, but it is very readable. Would there be a massive impact doing this twice if you have a rather large array?
Reduce:
const threeFourArr = arr.reduce((animArr, animal) => {
if (animal.length < 4) {
return [[...animArr[0], animal], animArr[1]];
} else {
return [animArr[0], [...animArr[1], animal]];
}
}, [[], []]);
Where the array's 0 index contains the array of less than four and the 1 index contains the array of more than three.
I don't like this too much, because it seems that the data structure is going to give a bit of problems, seeing that it is an array of arrays. I've thought about building an object with the reduce, but I can't imagine that it would be better than the array within an array solution.
I've managed to look at similar questions online as well as Stack Overflow, but many of these break the idea of immutability by using push() or they have very unreadable implementations, which in my opinion breaks the expressiveness of functional programming.
Are there any other ways of doing this? (functional of course)
collateBy
I just shared a similar answer here
I like this solution better because it abstracts away the collation but allows you to control how items are collated using a higher-order function.
Notice how we don't say anything about animal.length or < 4 or animals[0].push inside collateBy. This procedure has no knowledge of the kind of data you might be collating.
// generic collation procedure
const collateBy = f => g => xs => {
return xs.reduce((m,x) => {
let v = f(x)
return m.set(v, g(m.get(v), x))
}, new Map())
}
// custom collator
const collateByStrLen4 =
// collate by length > 4 using array concatenation for like elements
// note i'm using `[]` as the "seed" value for the empty collation
collateBy (x=> x.length > 4) ((a=[],b)=> [...a,b])
// sample data
const arr = ['horse','elephant','dog','crocodile','cat']
// get collation
let collation = collateByStrLen4 (arr)
// output specific collation keys
console.log('greater than 4', collation.get(true))
console.log('not greater than 4', collation.get(false))
// output entire collation
console.log('all entries', Array.from(collation.entries()))
Check out that other answer I posted to see other usage varieties. It's a pretty handy procedure.
bifilter
This is another solution that captures both out outputs of a filter function, instead of throwing away filtered values like Array.prototype.filter does.
This is basically what your reduce implementation does but it is abstracted into a generic, parameterized procedure. It does not use Array.prototype.push but in the body of a closure, localized mutation is generally accepted as OK.
const bifilter = (f,xs) => {
return xs.reduce(([T,F], x, i, arr)=> {
if (f(x, i, arr) === false)
return [T, [...F,x]]
else
return [[...T,x] ,F]
}, [[],[]])
}
const arr = ['horse','elephant','dog','crocodile','cat']
let [truthy,falsy] = bifilter(x=> x.length > 4, arr)
console.log('greater than 4', truthy)
console.log('not greater than 4', falsy)
Though it might be a little more straightforward, it's not nearly as powerful as collateBy. Either way, pick whichever one you like, adapt it to meet your needs if necessary, and have fun !
If this is your own app, go nuts and add it to Array.prototype
// attach to Array.prototype if this is your own app
// do NOT do this if this is part of a lib that others will inherit
Array.prototype.bifilter = function(f) {
return bifilter(f,this)
}
The function you are trying to build is usually known as partition and can be found under that name in many libraries, such as underscore.js. (As far as I know its not a builtin method)
var threeFourArr = _.partition(animals, function(x){ return x.length < 4 });
I don't like this too much, because it seems that the data structure is going to give a bit of problems, seeing that it is an array of arrays
Well, that is the only way to have a function in Javascript that returns two different values. It looks a bit better if you can use destructuring assignment (an ES6 feature):
var [smalls, bigs] = _.partition(animals, function(x){ return x.length < 4 });
Look at it as returning a pair of arrays instead of returning an array of arrays. "Array of arrays" suggests that you may have a variable number of arrays.
I've managed to look at similar questions online as well as Stack Overflow, but many of these break the idea of immutability by using push() or they have very unreadable implementations, which in my opinion breaks the expressiveness of functional programming.
Mutability is not a problem if you localize it inside a single function. From the outside its just as immutable as before and sometimes using some mutability will be more idiomatic than trying to do everything in a purely functional manner. If I had to code a partition function from scratch I would write something along these lines:
function partition(xs, pred){
var trues = [];
var falses = [];
xs.forEach(function(x){
if(pred(x)){
trues.push(x);
}else{
falses.push(x);
}
});
return [trues, falses];
}
A shorter .reduce() version would be:
const split = arr.reduce((animArr, animal) => {
animArr[animal.length < 4 ? 0 : 1].push(animal);
return animArr
}, [ [], [] ]);
Which might be combined with destructuring:
const [ lessThanFour, fourAndMore ] = arr.reduce(...)
If you are not opposed to using underscore there is a neat little function called groupBy that does exactly what you are looking for:
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
var results = _.groupBy(arr, function(cur) {
return cur.length > 4;
});
const greaterThanFour = results.true;
const lessThanFour = results.false;
console.log(greaterThanFour); // ["horse", "elephant", "crocodile"]
console.log(lessThanFour); // ["dog", "cat"]
Kudos for the beautiful response of the user Thank you, here an alternative using a recursion,
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
const splitBy = predicate => {
return x = (input, a, b) => {
if (input.length > 0) {
const value = input[0];
const [z, y] = predicate(value) ? [[...a, value], b] : [a, [...b, value]];
return x(input.slice(1), z, y);
} else {
return [a, b];
}
}
}
const splitAt4 = splitBy(x => x.length < 4);
const [lessThan4, fourAndMore ] = splitAt4(arr, [], []);
console.log(lessThan4, fourAndMore);
I don't think there could be another solution than returning an array of arrays or an object containing arrays. How else is a javascript function return multiple arrays after splitting them?
Write a function containing your push logic for readability.
var myArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var x = split(myArr, v => (v <= 5));
console.log(x);
function split(array, tester) {
const result = [
[],
[]
];
array.forEach((v, i, a) => {
if (tester(v, i, a)) result[0].push(v);
else result[1].push(v);
});
return result;
}

Categories