Quick access to deeply nested values in Objects in Javascript

Viewed 206

I have a data structure like this:

LAYOUT = {
  'Picking Type 1': {
    1: {
      1: {
        1: 'SKU1',
        2: 'SKU2',
        //etc
      },
      //etc
    },
    //etc
  },
  //etc
}

It corresponds to products in bins on shelves, so the keys are "Area": "Rack": "Level": "Bin": "Sku"

Trouble is, when a user updates one AreaRackLevelBin to a Sku, I need to find any other location that has that sku and remove it (a sku can only be on one place).

The naive way to do this is:

for (let area in layout)
  for (rack in layout[area])
    for (level in layout[area][rack])
      for (bin in layout[area][rack][level])
        if (layout[area][rack][level][bin] === sku)
          layout[area][rack][level][bin] = "";
          // call whatever code updates the backend

But this is obviously nested for looping. I did some back of the envelope math and calculated there will only ever be about 2,250 bins to traverse (5 areas, 10 racks, 9 levels, 5 bins).

Is there a way to traverse the deepest layer of a deeply nested object without 4 layers of for loops? Heck, I guess I'm not even sure if there's a performance hit when I would absolutely need to iterate over all 2,250 bins to check anyway. One thought I had was an array of pointers to those levels, but JavaScript doesn't have pointers. Any ideas?

UPDATE: The problem is not that I have to type LAYOUT[A][B][C][D], it's that I have to iterate all the keys in nested order to find the one value. This isn't even necessarily a problem if there isn't a more time efficient method.

My thought is that I could store an array of all the deepest objects and just iterate that. I imagine that would be a reference, not a value. It just doesn't seem explicit enough for me to trust I'm not modifying the values.

2 Answers

Editing to better illustrate what I was suggesting in the comments below. This is a what part of a very rudimentary implementation could look like:

const map = {}

const addAllSkusToMap = (layout) => {
  for (let area in layout)
    for (rack in layout[area])
      for (level in layout[area][rack])
        for (bin in layout[area][rack][level])
          addNewSkuToMap(sku, area, rack, level, bin)
}

const addNewSkuToMap = (sku, areaKey, rackKey, levelKey, binKey) => {
  map[sku] = { areaKey, rackKey, levelKey, binKey }
}

const getSkuLocation = (sku) => {
  return map[sku]
}

So after creating the map initially via 1 complete traversal of your object tree, if you want to get the location of a sku it's as simple as accessing map[sku] (which is O(1)).

You'll have to make sure to update map[sku]'s value when a user moves the sku to a different bin.

You have iterate throw your keys but you can do it effectively. Based on this like that:

let setValue = (propertyPath, value, obj) => {

let properties = Array.isArray(propertyPath) ? propertyPath : propertyPath.split(".")

  if (properties.length > 1) {
    if (!obj.hasOwnProperty(properties[0]) || typeof obj[properties[0]] !== "object") obj[properties[0]] = {}
    return setValue(properties.slice(1), value, obj[properties[0]])
  } else {
    obj[properties[0]] = value
    return true // this is the end
  }

}

let obj = {
  root: {
    child: {
      child1: {
        variable: null
      }
    }
  }
}

setValue("root.child.child1.variable", true, obj)

console.log(obj)

Related