Matthew Davis
Matthew Davis

Reputation: 25

Create 3D dimensional array

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?

Upvotes: 2

Views: 121

Answers (4)

Patrick Roberts
Patrick Roberts

Reputation: 51846

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"

Upvotes: 0

Patrick Roberts
Patrick Roberts

Reputation: 51846

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");
  }
}

Upvotes: 1

Lye Fish
Lye Fish

Reputation: 2568

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');

Upvotes: 4

bgoldst
bgoldst

Reputation: 35314

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

Upvotes: 0

Related Questions