2

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?

1
  • Even though you plan to use "indices" instead of "attributes" for accessing your values, I still think using nested objects is still more beneficial than using nested arrays, since you might not necessarily need values in intermediate indices, so arrays would waste more space than objects and require more strict range checking rather than just making nested objects on the fly when needed. See my ArrayND answer for a solution that uses nested objects. Commented Mar 8, 2015 at 20:37

4 Answers 4

4

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');
Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

0

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

Comments

0

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"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.