0

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.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Fred
  • 564
  • 10
  • 21
  • what do you mean with the last point (type)? – Nina Scholz Jul 16 '19 at 12:54
  • 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. – Fred Jul 16 '19 at 12:58
  • 3
    The last 2 bullet points are completely different requirements. So, it's better to ask them in separate posts. [One post with multiple questions or multiple posts?](https://meta.stackexchange.com/questions/39223) – adiga Jul 16 '19 at 13:01
  • 1
    `I find the laissez faire of Javascript difficult.` If this is the case there is https://www.typescriptlang.org/ or maybe https://flow.org/ – Keith Jul 16 '19 at 13:04
  • Thanks for suggesting Typescript and Flow. At the moment I'm investigating ReasonML. One day I'll post a question that clearly illustrates the benefits, when I find them. – Fred Jul 16 '19 at 18:09

5 Answers5

3

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));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

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)
claasic
  • 1,050
  • 6
  • 14
1

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.

Thomas
  • 11,958
  • 1
  • 14
  • 23
0

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))
Rajender Joshi
  • 4,155
  • 1
  • 23
  • 39
0

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))
Peter
  • 2,796
  • 1
  • 17
  • 29