What is the shortest way to create a 2d array of a certain size?
I figured that following code is pretty short, but can you do it with even less code?
var x = 5;
var y = 7;
var my2dArray = (new Array(y)).fill(0).map(
function(){
return new Array(x);
}
);
Array.apply(0, Array(x)).map(function() { return Array.apply(0, Array(y)); })
Simpler if you write a little convenience routine
function make_array(x) { return Array.apply(0, Array(x)); }
then
make_array(x).map(function() { return make_array(y);} )
or if you prefer
make_array(x).map(make_array.bind(0, y))
If you're writing ES6:
Array(...Array(x)).map(() => Array(y));
If you'd prefer to use fill, since you seem to have it available:
Array(x).fill().map(function() { return Array(y); });
or somewhat more concisely in ES6, using an arrow function:
Array(x).fill().map(() => Array(y));
It turns out Array#fill requires no argument; without it, it replaces absent elements with undefineds.
What about building your own Class ?
function Array2D(xSize, ySize, initialValue) {
initialValue=initialValue || 0;
// create an flat empty array filled with the initial value
var length = xSize*ySize;
var innerArray = new Array(length);
for (var i=0; i<length; i++) innerArray[i] = initialValue;
// accessors
this.getAt = function(x, y) { return innerArray[x+xSize*y]};
this.setAt = function(x, y, val) { innerArray[x+xSize*y]=val};
}
Use with :
var myArray2d = new Array2D(5,7);
myArray2d.setAt(1,1,3);
var value = myArray2d.getAt(1,1); // == 3
var anotherValue = myArray2d.getAt(2,2); // == 0
Related
I have a recursive function (performing Heap's algorithm to generate permutations). It seems to have a variable scope issue.
function permute(starting_arr) {
var all_permutations = [];
heap(starting_arr, starting_arr.length-1);
console.log(all_permutations);
return all_permutations;
function heap(a, n) { // a = array, n = max index i.e. length -1
var temp_an;
if (a.length-1 == n) {
all_permutations.push(a);
}
if (n>1) {
heap(a.slice(), n-1);
}
for ( var i=0; i<n ; i++ ) {
temp_an = a[n];
if (n%2==0) {
a[n] = a[0]
a[0] = temp_an;
} else {
a[n] = a[i]
a[i] = temp_an;
}
all_permutations.push(a);
if (n>1) { heap(a.slice(), n-1); }
}
}
}
It seems the problem is the "all_permutations.push(a)" because the thing works if I replace that with ...
all_permutations.push([]);
for ( var g=0 ; g<a.length ; g++ ) {
all_permutations[all_permutations.length-1][g] = a[g];
}
And as you can see I'm trying to use slice, which I've read about as a solution allowing you to pass the array by value rather than reference, such as in this ...
function x(arr){
arr.push(4);
}
var a = [1,2,3]
x(a);
console.log(a);
var b = [1,2,3]
x(b.slice());
console.log(b);
That does something but it still doesn't work properly.
Thanks for any help.
as you can see I'm trying to use slice, which I've read about as a solution allowing you to pass the array by value rather than reference
Yes, you found the problem and the correct approach for solving it. However you need to specifically apply it to the line
all_permutations.push(a);
in your for loop:
all_permutations.push(a.slice());
I have a C# script like below:
public List<MazePath> BreakIntoConnectedPaths()
{
List<MazeVertex> remainVertices = new List<MazeVertex>(vertices);
List<MazePath> paths = new List<MazePath>();
while (remainVertices.Count > 0)
{
MazePath path = new MazePath();
path.entrancePosition = entrancePosition;
path.exitPosition = exitPosition;
VisitCell(path, remainVertices.First(), null, remainVertices);
paths.Add(path);
//Store the coordinate for entrance and exit
}
return paths;
}
void VisitCell(MazePath path, MazeVertex ver, MazeVertex parent, List<MazeVertex> remainVertices)
{
remainVertices.Remove(ver);
path.Add(ver);
for (int i = 0; i < ver.connectVertices.Count; i++)
{
MazeVertex ver2 = ver.connectVertices[i];
if (ver2 != parent)
{
VisitCell(path, ver2, ver, remainVertices);
}
}
}
I want to convert it to javascript as below
BreakIntoConnectedPaths = function() {
var remainVertices = _.cloneDeep(this.vertices);
var paths = [];
while (remainVertices.length > 0) {
var path = new Path();
path.entrancePos = this.entrancePos;
path.exitPos = this.exitPos;
this.VisitCell(path, remainVertices[0], null, remainVertices);
paths.push(path);
// Store the coordinate for entrance and exit
}
return paths;
}
VisitCell = function(path, vertex, parentVertex, remainVertices) {
_.remove(remainVertices, function(v) {
return v.x === vertex.x && v.z === vertex.z;
});
path.Add(vertex);
for (var i = 0; i < vertex.connectVertices.length; i++) {
var connectedVertex = vertex.connectVertices[i];
// if (parentVertex && (connectedVertex.x !== parentVertex.x || connectedVertex.z !== parentVertex.z)) {
if(parentVertex && _.isEqual(connectedVertex, parentVertex)) {
VisitCell(path, connectedVertex, vertex, remainVertices);
}
}
}
The _ symbol here is lodash sign.
After I convert to javascript code, the behavior of these functions is difference with the C# one. With the same vertices data, the paths array had returned with difference size.
Thanks you for reading and pls help me if you see my mistake here.
In the C# version, your VisitCell function has a condition that says if(ver2 != parent), but in the JS version you check that they are equal instead of not equal.
Also, that condition would never pass any way because in your first call to that function you pass in null for the parent, but in that condition you check that the parent is "truthy".
Lodash's isEqual can handle null values, so I'm not sure why you're checking if the parent is truthy there. Perhaps you meant to do this?
if(!_.isEqual(connectedVertex, parentVertex)) {
There are several ways to improve your JavaScript code. When transpiling code, it is better to not copy/paste and fix, but to rewrite using the target language instead.
I would prefer to have this written, for example:
var vertices;
var entrancePos;
var exitPos;
function Path(entrancePos, exitPos){
this.entrancePos = entrancePos;
this.exitPos = exitPos;
this.Add = function() {
// your Add() code here
}
}
function breakIntoConnectedPaths() {
var remainingVertices = _.cloneDeep(vertices);
var paths = [];
while (remainVertices.length) {
var path = new Path(entrancePos, exitPos);
visitCell(path, remainingVertices.shift());
// Store the coordinate for entrance and exit
paths.push(path);
}
return paths;
}
function visitCell(path, vertex, parentVertex) {
path.Add(vertex);
for (var i = 0; i < vertex.connectVertices.length; i++) {
var connectedVertex = vertex.connectVertices[i];
if(_.isEqual(connectedVertex, parentVertex)) {
visitCell(path, connectedVertex, vertex);
}
}
}
Keep in mind that the variables vertices, entrancePos, exitPos and Path are not available to me on your C# code, so I only declare them on JavaScript. Implement them as you may.
Does that fix it, by the way?
I have an array of shots. I have been able to take that array and loop through it to get all shots that occurred on hole #1 and then rearrange them in order based on "shot_number". I now need to do this for every hole and to create an array for each hole (ex: holeArray1, holeArray2). I have attempted a number of solutions to increment x but if I do I end up missing some shots that occurred on certain holes.
How can I refactor this function to create this array for every hole without just copying and pasting the code and changing the variable x myself? Thank you for your help. I know I should be able to figure this one out but am struggling.
$scope.createHoleShotsArrays = function () {
var i = 0;
var x = 1;
var holeArray = [];
var len = $scope.shots.length;
for (; i < len; i++) {
if ($scope.shots[i].attributes.hole == x) {
holeArray.push($scope.shots[i]);
holeArray.sort(function (a, b) {
if (a.attributes.shot_number > b.attributes.shot_number) {
return 1;
}
if (a.attributes.shot_number < b.attributes.shot_number) {
return -1;
}
// a must be equal to b
return 0;
});
}
}
console.log(holeArray);
};
Push the items you want into arrays, and sort them once. I don't have cases to test the code. You may modified it a little if something goes wrong.
$scope.createHoleShotsArrays = function() {
var holeArrays = [];
$scope.shots.forEach(function(shot) {
if (holeArrays.length < shot.attributes.hole) {
holeArrays[shot.attributes.hole - 1] = [];
}
holeArrays[shot.attributes.hole - 1].push(shot);
});
holeArrays.forEach(function(arr) {
arr.sort(function(a, b) {
return a.attributes.shot_number - b.attributes.shot_number;
});
});
console.log(holeArrays);
};
In Javascript, I don't see any tutorials clearly explain how to create like
MyItems[Row][Index][categories]
so that
MyItems[0][0][0]=1
MyItems[1][0][0]='stock'
MyItems[5][1][0]='pending'
My use case is each Index will contain different value which is integer or string.
What is the best way to avoid error when accessing MyItems[0][1][0] that has no value?
Because JS doesn't have actual multidimensional arrays, but instead merely have nested arrays that don't necessarily form a rectangular structure, you'd need to check for each nested array first. A simple "truthy" test would be fine.
if (myItems[0] && myItems[0][0])
myItems[0][0].push(1);
If you wanted to create the arrays that aren't there, then you can do that like this:
if (!myItems[0])
myItems[0] = [];
if (!myItems[0][0])
myItems[0][0] = [];
myItems[0][0].push(1);
Of course this assumes that the first and second levels should always be arrays, and only the third level will hold the actual values. You'll need to adjust it if that's not the case.
Also, a function would be a good idea to get rid of the repetition.
function addNested(outer, idx1, idx2, idx3, value) {
if (!outer[idx1])
outer[idx1] = [];
if (!outer[idx1][idx2])
outer[idx1][idx2] = [];
outer[idx1][idx2][idx3] = value;
}
addNested(myItems, 1, 0, 0, 'stock');
This is how you'd make a 3D array, but I'd recommend against mixing data types in your array, that's not exactly a common or standard practice.
// just filler stuff, ignore the body of this function
function getStringOrNumber(row, col, cat) {
var thing = row * cols * cats + col * cats + cat;
return Math.random() < .5 ? thing : thing.toString();
}
// something to deal with each value
function doSomething(value) {
switch (typeof value) {
case 'string':
// logic for string type
break;
case 'number':
// logic for number type
break;
default:
// unexpected?
break;
}
}
// here's how you make your 3D array
var rows = 10,
cols = 10,
cats = 10,
array3d = new Array(rows),
i, j, k;
for (i = 0; i < rows; i++) {
array3d[i] = new Array(cols);
for (j = 0; j < cols; j++) {
array3d[i][j] = new Array(cats);
for (k = 0; k < cats; k++) {
array3d[i][j][k] = getStringOrNumber(i, j, k);
doSomething(array3d[i][j][k]);
}
}
}
If you want to check whether an index exists on the 3d array, try a function like this:
function setValue(array3d, row, col, cat, value) {
if (array3d[row] && array3d[row][col] && array3d[row][col][cat]) {
array3d[row][col][cat] = value;
} else {
throw new RangeError("Indices out of range");
}
}
If you were to allocate each array at each index in a breadth-first pattern before accessing any of it, then this would work without any special handling.
However, as you've correctly recognized, if you want to be able to access indexes that may not have been allocated yet, this won't work.
Actually, to be more specific, you are allowed to attempt to read an index outside the length of an array, in which case you'll get undefined. The problem is that if you get undefined for the first or second depth, then an attempt to index that undefined value will fail.
Thus, to prevent this error, you must guard against undefined first- or second-depth indexes.
The best way to do this is to write a class that provides a getter and setter that automatically take care of the special handling requirements. Here's an example of such a class, defined using the prototype pattern:
(function() {
var Array3D = function() {
this.data = [];
};
Array3D.prototype.get = function(r,c,z) {
if (this.data.length <= r) return undefined;
if (this.data[r].length <= c) return undefined;
return this.data[r][c][z];
};
Array3D.prototype.set = function(r,c,z,v) {
if (this.data.length <= r) this.data[r] = [];
if (this.data[r].length <= c) this.data[r][c] = [];
this.data[r][c][z] = v;
return this;
};
window.Array3D = Array3D;
})();
var a = new Array3D();
alert(a.get(0,0,0)); // undefined, no error
a.set(0,0,0,'x');
alert(a.get(0,0,0)); // 'x'
a.set(234,1234,342,'y');
alert(a.get(234,1234,342)); // 'y'
alert(a.get(0,1,0)); // undefined, no error
alert(a.get(12341234,243787,234234)); // undefined, no error
Since this completely differs from my other answer, I thought it would be helpful to suggest another approach using nested sparse arrays which could be implemented using associative arrays or objects. Try this:
// N-dimensional array
function ArrayND() {
// nothing to do here, seriously
}
ArrayND.prototype.setValue = function (value) {
var indices = arguments,
nest = this,
index, i;
// note the range of values since the last recursion is being set to a value
for (i = 1; i < indices.length - 2; i++) {
index = indices[i];
if (nest[index] instanceof ArrayND) {
nest = nest[index];
} else if (typeof nest[index] === "undefined") {
// recursive functionality!
nest = nest[index] = new ArrayND();
} else {
// we don't want to get rid of this value by accident!
return false;
}
}
// now "nest" is equal to the ArrayND you want to set the value inside of
index = indices[i];
nest[index] = value;
// we set the value successfully!
return true;
}
ArrayND.prototype.getValue = function () {
var indices = arguments,
nest = this,
index, i;
// note the range because we're getting the last value
for (i = 0; i < indices.length; i++) {
index = indices[i];
// for last recursion, just has to exist, not be ArrayND
if (nest[index]) {
nest = nest[index];
} else {
// nothing is defined where you're trying to access
return undefined;
}
}
return nest;
}
var arrayND = new ArrayND();
arrayND.setValue(1, 0, 0, 0);
arrayND.setValue("stock", 1, 0, 0);
arrayND.setValue("pending", 5, 1, 0);
// you can treat it like a normal 3D array if you want
console.log(arrayND[0][0][0]); // 1
console.log(arrayND[1][0][0]); // "stock"
console.log(arrayND[5][1][0]); // "pending"
// or use a nicer way to get the values
console.log(arrayND.getValue(1, 0, 0)); // "stock"
// phew, no errors!
console.log(arrayND.getValue(3, 1, 0)); // undefined
// some awesome recursive functionality!
console.log(arrayND.getValue(5).getValue(1).getValue(0)); // "pending"
is there a way to automatically create subobjects in an assignment after construction, i.e.
var obj = {};
obj.a.b.c=13;
the above gives me a "obj.a is undefined" error
i wrote a function to do this, but wondered if there was an easier way
_setObjectProperty(obj,13,['a','b','c']);
function _setObjectProperty(obj,value,loc)
{
if(loc.length>1) {
obj[loc[0]] = obj[loc[0]] || {};
_setObjectProperty(obj[loc[0]],value,loc.splice(1));
}
else if(loc.length===1) {
obj[loc[0]]=value;
}
}
No, there's no built in way to do this in JavaScript. The only way is to create your own function like you did. If you want the convenience of the dot operator/notation you can use the following function:
var set = function(path, value, root) {
var segments = path.split('.'),
cursor = root || window,
segment,
i;
for (i = 0; i < segments.length - 1; ++i) {
segment = segments[i];
cursor = cursor[segment] = cursor[segment] || {};
}
return cursor[segments[i]] = value;
};
set("a.b.c", 2);
console.log(a.b.c) // => 2