Trying to create a function mCreate()
that given a set a numbers returns a multidimensional array (matrix):
mCreate(2, 2, 2)
// [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]
When this functions handles just 2 levels of depth ie: mCreate(2, 2) //[[0, 0], [0, 0]]
I know to do 2 levels, you can use 2 nested for loops
but the problem I'm having is how to handle an n'th number of arguments.
Would this problem be better approached with recursion, otherwise how can I dynamically determine the number of nested for loops
I'm going to need given the number of arguments?
ps: the most performant way would be great but not essential
RE-EDIT - After using Benchmark.js to check perf the results were as follows:
BenLesh x 82,043 ops/sec ±2.56% (83 runs sampled)
Phil-P x 205,852 ops/sec ±2.01% (81 runs sampled)
Brian x 252,508 ops/sec ±1.17% (89 runs sampled)
Rick-H x 287,988 ops/sec ±1.25% (82 runs sampled)
Rodney-R x 97,930 ops/sec ±1.67% (81 runs sampled)
Fastest is Rick-H
@briancavalier also came up with a good solution JSbin:
const mCreate = (...sizes) => (initialValue) => _mCreate(sizes, initialValue, sizes.length-1, 0)
const _mCreate = (sizes, initialValue, len, index) =>
Array.from({ length: sizes[index] }, () =>
index === len ? initialValue : _mCreate(sizes, initialValue, len, index+1))
mCreate(2, 2, 2)(0)
A multi-dimensional array is an array with more than one level or dimension. For example, a 2D array, or two-dimensional array, is an array of arrays, meaning it is a matrix of rows and columns (think of a table).
Here is how we can initialize a 2-dimensional array in Java. int[][] a = { {1, 2, 3}, {4, 5, 6, 9}, {7}, }; As we can see, each element of the multidimensional array is an array itself. And also, unlike C/C++, each row of the multidimensional array in Java can be of different lengths.
The general syntax to declare 3D array in java is as follows: data-type[ ][ ][ ] variableName; variableName = new data-type[size1][size2][size3]; Or, data-type[ ][ ][ ] variableName = new data-type[size1][size2][size3]; Here, data-type refers to any data type supported by Java.
The general form of declaring N-dimensional arrays is:data_type array_name[size1][size2]....[sizeN]; data_type: Type of data to be stored in the array. array_name: Name of the array. size1, size2,… ,sizeN: Sizes of the dimension.
One simple recursive answer is this (in ES2015):
const mCreate = (...sizes) =>
Array.from({ length: sizes[0] }, () =>
sizes.length === 1 ? 0 : mCreate(...sizes.slice(1)));
JS Bin here
EDIT: I think I'd add the initializer in with a higher order function though:
const mCreate = (...sizes) => (initialValue) =>
Array.from({ length: sizes[0] }, () =>
sizes.length === 1 ? initialValue : mCreate(...sizes.slice(1))(initialValue));
Which could be used like:
mCreate(2, 2, 2)('hi');
// [[["hi", "hi"], ["hi", "hi"]], [["hi", "hi"], ["hi", "hi"]]]
JSBin of that
Here's a non-recursive solution:
function mCreate() {
var result = 0, i;
for(i = arguments.length - 1; i >= 0 ; i--) {
result = new Array(arguments[i]).fill(result);
}
return JSON.parse(JSON.stringify(result));
}
The JSON functions are used to mimic a deep clone, but that causes the function to be non-performant.
function mCreate() {
var result = 0, i;
for(i = arguments.length - 1; i >= 0 ; i--) {
result = new Array(arguments[i]).fill(result);
}
return JSON.parse(JSON.stringify(result));
}
console.log(JSON.stringify(mCreate(2, 2, 2)));
console.log(JSON.stringify(mCreate(1, 2, 3, 4)));
console.log(JSON.stringify(mCreate(5)));
console.log(JSON.stringify(mCreate(1, 5)));
console.log(JSON.stringify(mCreate(5, 1)));
var m = mCreate(1, 2, 3, 4);
m[0][1][1][3] = 4;
console.log(JSON.stringify(m));
Recursive algorithms may be easier to reason about, but generally they're not required. In this particular case the iterative approach is simple enough.
Your problem consists of two parts:
0
-value elementsHere's an implementation of what I think you're trying to create:
function nested() {
// handle the deepest level first, because we need to generate the zeros
var result = [];
for (var zeros = arguments[arguments.length - 1]; zeros > 0; zeros--) {
result.push(0);
}
// for every argument, walking backwards, we clone the
// previous result as often as requested by that argument
for (var i = arguments.length - 2; i >= 0; i--) {
var _clone = [];
for (var clones = arguments[i]; clones > 0; clones--) {
// result.slice() returns a shallow copy
_clone.push(result.slice(0));
}
result = _clone;
}
if (arguments.length > 2) {
// the shallowly copying the array works fine for 2 dimensions,
// but for higher dimensions, we need to compensate
return JSON.parse(JSON.stringify(result));
}
return result;
}
Since writing the algorithm is only half of the solution, here's the test to verify our function actually performs the way we want it to. We'd typically use one of the gazillion test runners (e.g. mocha or AVA). But since I don't know your setup (if any), we'll just do this manually:
var tests = [
{
// the arguments we want to pass to the function.
// translates to nested(2, 2)
input: [2, 2],
// the result we expect the function to return for
// the given input
output: [
[0, 0],
[0, 0]
]
},
{
input: [2, 3],
output: [
[0, 0, 0],
[0, 0, 0]
]
},
{
input: [3, 2],
output: [
[0, 0],
[0, 0],
[0, 0]
]
},
{
input: [3, 2, 1],
output: [
[
[0], [0]
],
[
[0], [0]
],
[
[0], [0]
]
]
},
];
tests.forEach(function(test) {
// execute the function with the input array as arguments
var result = nested.apply(null, test.input);
// verify the result is correct
var matches = JSON.stringify(result) === JSON.stringify(test.output);
if (!matches) {
console.error('failed input', test.input);
console.log('got', result, 'but expected', rest.output);
} else {
console.info('passed', test.input);
}
});
It's up to you to define and handle edge-cases, like nested(3, 0)
, nested(0, 4)
, nested(3, -1)
or nested(-1, 2)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With