How to build a JSON key for looking up value dynamically

Viewed 71

Background

I am creating an engine to pick up values from different JSON files, each with different structure. I am looking for ways to store the key as string (or array) in a static file, and use the key to get the value.

Keys in string or array form that are going to store in a static file may look like hello.world.that.is.something and that.is.something.different or in array ["hello", "world", "that", "is", "something"] and ["that", "is", "something", "different"].

Given the sample data below, is there any way that I can retreive the value [1, 2, 3, 4, 5] and Noo!!! from prebuild-keys (stored somewhere)?

Sample Data

let data = {
  hello: {
    world: {
      that: {
        is: {
          something: [1, 2, 3, 4, 5]
        }
      }
    }
  },
  that: {
    is: {
      something: {
        different: "Noo!!!"
      }
    }
  }
}

Expected Data

data[pre_build_keys_1] // [1, 2, 3, 4, 5]
data[pre_build_keys_2] // Noo!!!
4 Answers

Assuming the data structure is stable and consistent with the example I think you've done all the hard work already! At this point you can just evaluate the exact path you want and return that.

console.log(eval("data.hello.world.that.is.something"));
console.log(eval("data.that.is.something.different"));

You can create function like getData and pass data object with key array like below. Click here tO know more about reduce used in getData.

function getData(data, keys) {
  return keys.reduce((acc, key) => acc[key], data);
}

let pre_build_keys_1 = ["hello", "world", "that", "is", "something"];
let pre_build_keys_2 = ["that", "is", "something", "different"]

let data = {
  hello: {
    world: {
      that: {
        is: {
          something: [1, 2, 3, 4, 5]
        }
      }
    }
  },
  that: {
    is: {
      something: {
        different: "Noo!!!"
      }
    }
  }
};

console.log(getData(data, pre_build_keys_1)); // [1, 2, 3, 4, 5]
console.log(getData(data, pre_build_keys_2)); // Noo!!!

You can use eval() which it does evaluate a string and treat it as a node / variable, assuming that you have declared a variable name that is equivalent to the evaluated string.

let data = {
  hello: {
    world: {
      that: {
        is: {
          something: [1, 2, 3, 4, 5]
        }
      }
    }
  },
  that: {
    is: {
      something: {
        different: "Noo!!!"
      }
    }
  }
}

let pre_build_keys_1 = "data.hello.world.that.is.something"
let pre_build_keys_2 = "data.that.is.something.different"

console.log(eval(pre_build_keys_1))
console.log(eval(pre_build_keys_2))

You could create a proxy object that will handle getting the appropriate item from the object, and then you can literally do data['hello.world.that.is.something'] to get the thing you want, like so:

let real_data = {
  hello: {
    world: {
      that: {
        is: {
          something: [1, 2, 3, 4, 5]
        }
      }
    }
  },
  that: {
    is: {
      something: {
        different: "Noo!!!"
      }
    }
  }
}

const handler = {
  get: function(target, prop, receiver) {
    let parsed;
    try {
        parsed = JSON.parse(prop);
    } catch (e) {
      if (prop.startsWith('[')) {
          parsed = prop.substring(1, prop.length - 1);
          parsed = parsed.split(', ').join(',').split(',');
      } else {
          parsed = prop.split('.');
      }
    }
    return parsed.reduce((carry, current) => carry[current], target);
  }
};

const data = new Proxy(real_data, handler);


console.log(data['hello.world.that.is.something'])
console.log(data['[that, is, something, different]'])
console.log(data['["that", "is", "something", "different"]'])

Related