• index.js

  • ¶

    Dotty makes it easy to programmatically access arbitrarily nested objects and their properties.

  • ¶

    object is an object, path is the path to the property you want to check for existence of.

    path can be provided as either a "string.separated.with.dots" or as ["an", "array"].

    Returns true if the path can be completely resolved, false otherwise.

    var exists = module.exports.exists = function exists(object, path) {
      if (typeof path === "string") {
        path = path.split(".");
      }
    
      if (!(path instanceof Array) || path.length === 0) {
        return false;
      }
    
      path = path.slice();
    
      var key = path.shift();
    
      if (typeof object !== "object" || object === null) {
        return false;
      }
    
      if (path.length === 0) {
        return Object.hasOwnProperty.apply(object, [key]);
      } else {
        return exists(object[key], path);
      }
    };
  • ¶

    These arguments are the same as those for exists.

    The return value, however, is the property you’re trying to access, or undefined if it can’t be found. This means you won’t be able to tell the difference between an unresolved path and an undefined property, so you should not use get to check for the existence of a property. Use exists instead.

    var get = module.exports.get = function get(object, path) {
      if (typeof path === "string") {
        path = path.split(".");
      }
    
      if (!(path instanceof Array) || path.length === 0) {
        return;
      }
    
      path = path.slice();
    
      var key = path.shift();
    
      if (typeof object !== "object" || object === null) {
        return;
      }
    
      if (path.length === 0) {
        return object[key];
      }
    
      if (path.length) {
        return get(object[key], path);
      }
    };
  • ¶

    Arguments are similar to exists and get, with the exception that path components are regexes with some special cases. If a path component is "*" on its own, it’ll be converted to /.*/.

    The return value is an array of values where the key path matches the specified criterion. If none match, an empty array will be returned.

    If an action function is specified, that action will be applied to each match. Action params are value, parent and key.

    var search = module.exports.search = function search(object, path, action) {
      if (typeof path === "string") {
        path = path.split(".");
      }
    
      if (!(path instanceof Array) || path.length === 0) {
        return;
      }
    
      path = path.slice();
    
      var key = path.shift();
    
      if (typeof object !== "object" || object === null) {
        return;
      }
    
      if (key === "*") {
        key = ".*";
      }
    
      if (typeof key === "string") {
        key = new RegExp(key);
      }
    
      if (path.length === 0) {
        return Object.keys(object).filter(key.test.bind(key)).map(function(k) {
          var value = object[k];
          if(action){
            action(value, object, k);
          }
          return value;
        });
      } else {
        return Array.prototype.concat.apply([], Object.keys(object).filter(key.test.bind(key)).map(function(k) { return search(object[k], path, action); }));
      }
    };
  • ¶

    Perform a search and remove the matched keys. The return value is the same object argument with modifications.

    var removeSearch = module.exports.removeSearch = function removeSearch(object, path){
      search(object, path, function(value, object, key){
        delete object[key];
      });
      return object;
    };
  • ¶

    The first two arguments for put are the same as exists and get.

    The third argument is a value to put at the path of the object. Objects in the middle will be created if they don’t exist, or added to if they do. If a value is encountered in the middle of the path that is not an object, it will not be overwritten.

    The return value is true in the case that the value was put successfully, or false otherwise.

    var put = module.exports.put = function put(object, path, value) {
      if (typeof path === "string") {
        path = path.split(".");
      }
    
      if (!(path instanceof Array) || path.length === 0) {
        return false;
      }
      
      path = path.slice();
    
      var key = path.shift();
    
      if (typeof object !== "object" || object === null) {
        return false;
      }
    
      if (path.length === 0) {
        object[key] = value;
      } else {
        if (typeof object[key] === "undefined") {
          object[key] = {};
        }
    
        if (typeof object[key] !== "object" || object[key] === null) {
          return false;
        }
    
        return put(object[key], path, value);
      }
    };
  • ¶

    remove is like put in reverse!

    The return value is true in the case that the value existed and was removed successfully, or false otherwise.

    var remove = module.exports.remove = function remove(object, path, value) {
      if (typeof path === "string") {
        path = path.split(".");
      }
    
      if (!(path instanceof Array) || path.length === 0) {
        return false;
      }
      
      path = path.slice();
    
      var key = path.shift();
    
      if (typeof object !== "object" || object === null) {
        return false;
      }
    
      if (path.length === 0) {
        if (!Object.hasOwnProperty.call(object, key)) {
          return false;
        }
    
        delete object[key];
    
        return true;
      } else {
        return remove(object[key], path, value);
      }
    };
  • ¶

    deepKeys creates a list of all possible key paths for a given object.

    The return value is always an array, the members of which are paths in array format. If you want them in dot-notation format, do something like this:

    dotty.deepKeys(obj).map(function(e) {
      return e.join(".");
    });
    

    Note: this will probably explode on recursive objects. Be careful.

    var deepKeys = module.exports.deepKeys = function deepKeys(object, options, prefix) {
      options = options || {};
    
      if (typeof prefix === "undefined") {
        prefix = [];
      }
    
      var keys = [];
    
      for (var k in object) {
        if (!Object.hasOwnProperty.call(object, k)) {
          continue;
        }
    
        if (!options.leavesOnly || typeof object[k] !== "object") {
          keys.push(prefix.concat([k]));
        }
    
        if (typeof object[k] === "object" && object[k] !== null) {
          keys = keys.concat(deepKeys(object[k], {leavesOnly: options.leavesOnly}, prefix.concat([k])));
        }
      }
    
      if (options.asStrings) {
        keys = keys.map(function(e) { return e.join("."); });
      }
    
      return keys;
    };