Newsworthy Presidential Mistakes

    keypather

    3.1.0 • Public • Published

    keypather-logo

    keypather Build Status Coverage Status js-standard-style

    Get, set, or delete a deep value using a keypath string.

    A collection of keypath utilities: get, set, delete, in, has, flatten, expand, and immutable set/delete.

    Lightweight and parses keypaths using vanilla JS - No eval or new Function hacks!

    Installation

    npm install keypather

    Usage

    Examples

    Import

    // modular imports, so you can keep your bundle lean
    const get = require('keypather/get')
    const set = require('keypather/set')
    const del = require('keypather/del')
    const immutableSet = require('keypather/immutable-set')
    const immutableDel = require('keypather/immutable-del')
    const keypathIn = require('keypather/in')
    const hasKeypath = require('keypather/has')
    const expand = require('keypather/expand')
    const flatten = require('keypather/flatten')

    GET, SET, DEL Example

    const get = require('keypather/get')
    const set = require('keypather/set')
    const del = require('keypather/del')
     
    let obj
     
    // Objects
    obj = { foo: { bar: 100 } }
    get(obj, 'foo.bar')          // returns 100
    del(obj, '["foo"]["bar"]')   // returns true, obj becomes { foo: {} }
    set(obj, 'foo.bar.qux', 200) // returns 200, obj becomes { foo: { bar: { qux: 200 } } }
    get(obj, 'foo["bar"].qux')   // returns 200
     
    // Arrays
    obj = {}
    set(obj, 'foo[0]', 100)      // obj is { foo: [ 100 ] }

    Immutable SET, DEL Example

    const set = require('keypather/immutable-set')
    const del = require('keypather/immutable-del')
     
    let obj
    let out
     
    // Objects
    obj = { foo: { bar: 100 } }
    out = set(obj, 'foo.bar', 100)     // returns obj
    // out === obj,
    // since it was not modified
    out = set(obj, 'foo.bar.qux', 200) // returns { foo: { bar: { qux: 200 } } }
    // out !== obj,
    // obj is still { foo: { bar: 100 } }
    out = del(obj, 'one.two.three')    // returns obj
    // out === obj,
    // since it was not modified
    out = del(obj, 'foo.bar.qux')     // returns { foo: { bar: {} } }
    // out !== obj,
    // obj is still { foo: { bar: { qux: 200 } } }
     
    // Arrays
    obj = {}
    out = set(obj, 'foo[0]', 100) // returns { foo: [ 100 ] } (new)
    // out !== obj, obj is still { foo: { bar: 100 } }

    HAS, IN Example

    const hasKeypath = require('keypather/has')
    const keypathIn = require('keypather/in')
     
    const obj = { foo: Object.create({ bar: 100 }) }
     
    hasKeypath(obj, 'foo.bar') // returns false (bar is on proto)
    keypathIn(obj, 'foo.bar')  // returns true
    hasKeypath(obj, 'foo')     // returns true

    FLATTEN, EXPAND Example

    const expand = require('keypather/expand')
    const flatten = require('keypather/flatten')
     
    const obj = expand({
      'foo.bar': 1,
      'foo.qux[0]': 100,  
      'foo["qux"][1]': 200,
      'foo.qux.wut': 'val'
    })
    // obj is { foo { bar: 1, qux: [ 100, 200, wut: 'val' ] } }
    const flat = flatten(obj)
    // flat is { 'foo.bar': 1, 'foo.qux': 2 } }

    Errors Example

    /* Missing deep values w/ "force: false" */
    get({}, 'foo.bar', { force: false })
    set({}, 'foo.bar', 100, { force: false })
    del({}, 'foo.bar', { force: false })
    immutableSet({}, 'foo.bar', 100, { force: false })
    immutableDel({}, 'foo.bar', { force: false })
    // TypeError: Cannot read property 'bar' of undefined (at keypath 'foo' of 'foo.bar')
    get({ foo: {} }, 'foo.bar', { force: false })
    set({ foo: {} }, 'foo.bar', 100, { force: false })
    del({ foo: {} }, 'foo.bar', { force: false })
    immutableSet({ foo: {} }, 'foo.bar', 100, { force: false })
    immutableDel({ foo: {} }, 'foo.bar', { force: false })
    // TypeError: Cannot read property 'bar' of undefined (at keypath 'foo.bar' of 'foo.bar.qux')
    hasKeypath({}, 'foo.bar', { force: false })
    // TypeError: Cannot read property 'hasOwnProperty' of undefined (hasOwnProperty('bar') errored at keypath 'foo' of 'foo.bar')
    keypathIn({}, 'foo.bar', { force: false })
    // TypeError: Cannot use 'in' operator to search for 'bar' in undefined (at 'foo' of 'foo.bar')
    keypathIn({}, 'foo.bar.qux', { force: false })
    hasKeypath({}, 'foo.bar.qux', { force: false })
    // TypeError: Cannot read property 'bar' of undefined (at keypath 'foo' of 'foo.bar.qux')
     
    /* Warnings for set and immutable-set */
    // by default, set will overwrite primitives (string, number or regexp) to an object or array.
    // when overwritePrimitives is set to false, sets will warn when settings a key on a primitive
    // to disable all warnings use the option { warn: false }
    set({}, '[0]', 'val', { overwritePrimitives: false })
    // log: Setting number key (0) on object at keypath '' of '[0]')
    set([], 'key', 'val', { overwritePrimitives: false })
    // log: Setting string key 'foo' on array at keypath '' of 'foo')
    set({ foo: 1 }, 'foo.qux', 'val', { overwritePrimitives: false })
    // log: Setting key 'qux' on number 1 at keypath 'foo' of 'foo.qux')
    set({ foo: 1 }, 'foo[0]', 'val', { overwritePrimitives: false })
    // log: Setting number key (0) on number 1 at keypath 'foo' of 'foo[0]')
    set({ foo: 'str' }, 'foo.bar', 'val', { overwritePrimitives: false })
    // log: Setting key 'bar' on string 'str' at keypath 'foo' of 'foo.bar')
    set({ foo: {} }, 'foo[0]', 'val', { overwritePrimitives: false })
    // log: Setting number key (0) on object at keypath 'foo' of 'foo[0]')
     
    /* Invalid keypaths */
    get({}, 'foo.1bar')
    // Error: Unexpected token '1' in keypath 'foo.1bar' at position 4 (invalid dot key)
    get({}, 'foo[]')       
    // Error: Unexpected token ']' in keypath 'foo[]' at position 4 (invalid bracket key)
    get({}, 'foo["]')
    // Error: Unexpected token ']' in keypath 'foo[]' at position 5 (invalid bracket string key)
    get({}, 'foo.')
    // Error: Unexpected end of keypath 'foo.' (invalid dot key)
    get({}, 'foo[')
    // Error: Unexpected end of keypath 'foo[' (invalid bracket key)
    get({}, "foo['")
    // Error: Unexpected end of keypath 'foo['' (invalid bracket string key)

    Documentation

    GET

    Returns value at keypath in obj

    • @param {any} obj - context to read keypath from
    • @param {string} keypath - bracket and/or dot notation keypath string
    • @param {?object} opts - optional, defaults to { force: true }
    • opts.force - force specifies whether non-existant keypaths should be ignored, defaults to true
    • if false, `get` will error when reading a key on a non-existant keypath.
      
    • @returns {any} value at keypath
    const get = require('keypather/get');
    const obj = {
      foo: {
        bar: {
          baz: 'val'
        }
      }
    };
    get(obj, "foo.bar.baz");           // returns 'val'
    get(obj, "foo['bar'].baz");        // returns 'val'
    get(obj, "['foo']['bar']['baz']"); // returns 'val'
     
    get({}, 'foo.two.three', { force: false }) // throws error
    // TypeError: Cannot read property 'three' of undefined (at keypath 'foo.two' of 'foo.two.three')

    SET

    Sets a value in obj at keypath. If force=true, set will create objects at non-existant keys in the keypath. If the non-existant key is a number, its value will be initialized as an array.

    • @param {any} obj - context to read keypath from
    • @param {string} keypath - bracket and/or dot notation keypath string to read from obj
    • @returns {any} value - value to set at keypath
    • @param {?object} opts - optional, defaults to { force: true, overwritePrimitives: true, warn: true }
    • opts.force - whether non-existant keys in keypath should be created, defaults to true.
    • if false, `set` will error when reading a key on a non-existant keypath.
      
    • opts.overwritePrimitives - whether primitive keys (booleans, strings, numbers) should be overwritten.
    • setting a key on a primitive will convert it to an object or array (if key is string or number).
      
    • if false, `set` will log a warning when setting keys on primitives.
      
    • opts.silent - specifies whether warning logs should be enabled, defaults to false.
    • @returns {any} value set at keypath
    const set = require('keypather/set');
     
    let obj = {
      foo: {
        bar: {
          baz: 'val'
        }
      }
    };
    set(obj, "foo['bar'].baz", 'val');        // returns 'val'
    set(obj, "foo.bar.baz", 'val');           // returns 'val'
    set(obj, "['foo']['bar']['baz']", 'val'); // returns 'val'
     
    /* By default, set forces creation of non-existant keys */
    obj = {}
    set(obj, "foo.bar.baz", 'val'); // returns 'val'
    // obj becomes:
    // {
    //   foo: {
    //     bar: {
    //       baz: 'val'
    //     }
    //   }
    // };
     
    /* By default, overwrites primitives when setting a key on one */
    obj = { foo: 1 }
    set(obj, "foo.bar.baz", 'val'); // returns 'val'
    // obj becomes:
    // {
    //   foo: {
    //     bar: {
    //       baz: 'val'
    //     }
    //   }
    // };
    obj = { foo: 1 }
    set(obj, "foo[0].baz", 'val'); // returns 'val'
    // obj becomes:
    // {
    //   foo: [{
    //     baz: 'val'
    //   }]
    // };
     
    /* Errors, force=false */
    set({}, "foo.bar.baz", 'val', { force: false }); // throw's an error
    // TypeError: Cannot read property 'bar' of undefined (at keypath 'foo' of 'foo.bar.baz')
    // see more errors above in the 'Errors' section
     
    /* Warnings, overwritePrimitives=false */
    set({ foo: 'str' }, 'foo.bar', 'val', { overwritePrimitives: false })
    // log: Setting key 'bar' on string 'str' at keypath 'foo' of 'foo.bar')
    // see more warnings above in the 'Errors' section

    DEL

    Deletes value a keypath in obj. Similar to delete obj.key.

    • @param {any} obj - context to read keypath from
    • @param {string} keypath - bracket and/or dot notation keypath string to delete from obj
    • @param {?object} opts - optional, defaults to { force: true }
    • opts.force - whether non-existant keys in keypath should be created, defaults to true.
    • if false, `del` will error when reading a key on a non-existant keypath.
      
    • @returns {boolean} true except when the property is non-configurable or in non-strict mode
    const del = require('keypather/del');
     
    const obj = {
      foo: {
        bar: {
          baz: 'val'
        }
      }
    };
    del(obj, "foo['bar'].baz");        // true
    del(obj, "foo.bar.baz");           // true
    del(obj, "['foo']['bar']['baz']"); // true
    // obj becomes:
    // {
    //   foo: {
    //     bar: {}
    //   }
    // }
     
    /* Errors, force=false */
    del(obj, "one.two.three", 'val', { force: false }); // throw's an error
    // TypeError: Cannot read property 'two' of undefined (at keypath 'one' of 'one.two.three')
    // see more errors above in the 'Errors' section

    IMMUTABLE SET

    Sets a value in obj at keypath. If force=true, set will create objects at non-existant keys in the keypath. If the non-existant key is a number, its value will be initialized as an array.

    • @param {any} obj - context to read keypath from
    • @param {string} keypath - bracket and/or dot notation keypath string to read from obj
    • @returns {any} value - value to set at keypath
    • @param {?object} opts - optional, defaults to { force: true, overwritePrimitives: true, warn: true }
    • opts.force - whether non-existant keys in keypath should be created, defaults to true.
    • if false, `immutable-set` will error when reading a key on a non-existant keypath.
      
    • opts.overwritePrimitives - whether primitive keys (booleans, strings, numbers) should be overwritten.
    • setting a key on a primitive will convert it to an object or array (if key is string or number).
      
    • if false, `immutable-set` will log a warning when setting keys on primitives.
      
    • opts.silent - specifies whether warning logs should be enabled, defaults to false.
    • opts.shallowClone - provide custom shallowClone, defaults to shallow-clone
    • @returns {any} returns same obj if unmodified, otherwise modified clone of obj
    const set = require('keypather/immutable-set');
     
    let obj = {
      foo: {
        bar: {
          baz: 'val'
        }
      }
    };
    let out
    out = set(obj, "foo['bar'].baz", 'val');         // returns SAME object, since the value was unchanged
    // out === obj
    out = set(obj, "foo.bar.baz", 'val2');           // returns { foo: { bar: { baz: 'val2' } } } (new object)
    // out !== obj
    out = set(obj, "['foo']['bar']['baz']", 'val3'); // returns { foo: { bar: { baz: 'val3' } } } (new object)
    // out !== obj
     
    /* By default, overwrites primitives when setting a key on one */
    obj = { foo: 1 }
    out = set(obj, "foo.bar.baz", 'val'); // returns new object
    // out !== obj
    // out is:
    // {
    //   foo: {
    //     bar: {
    //       baz: 'val'
    //     }
    //   }
    // };
    obj = { foo: 1 }
    out = set(obj, "foo[0].baz", 'val'); // returns new object
    // out !== obj
    // out is:
    // {
    //   foo: [{
    //     baz: 'val'
    //   }]
    // };
     
    /* Errors, force=false */
    obj = {}
    set(obj, "foo.bar.baz", 'val', { force: false }); // throws error
    // Error: Cannot read property 'bar' of undefined (at keypath 'foo' of 'foo.bar.baz')
     
    /* Warnings, force=false */
    obj = { foo: 'str' }
    out = set(obj, 'foo.bar', 'val', { overwritePrimitives: false })
    // out === obj, since keys cannot be set on strings or numbers
    // log: Setting key 'bar' on string 'str' at keypath 'foo' of 'foo.bar')

    IMMUTABLE DEL

    Deletes value a keypath in obj. Similar to delete obj.key.

    • @param {any} obj - context to read keypath from
    • @param {string} keypath - bracket and/or dot notation keypath string to delete from obj
    • @param {?object} opts - optional, defaults to { force: true }
    • opts.force - whether non-existant keys in keypath should be created, defaults to true.
    • if false, `del` will error when reading a key on a non-existant keypath.
      
    • opts.shallowClone - provide custom shallowClone, defaults to shallow-clone
    • @returns {any} returns same obj if unmodified, otherwise modified clone of obj
    const del = require('keypather/immutable-del');
    const obj = {
      foo: {
        bar: {
          baz: 'val'
        }
      }
    };
    let out
    out = del(obj, "foo['bar'].baz");        // true
    out = del(obj, "foo.bar.baz");           // true
    out = del(obj, "['foo']['bar']['baz']"); // true
    // obj becomes:
    // {
    //   foo: {
    //     bar: {}
    //   }
    // }
     
    /* Errors, force=false */
    del(obj, "one.two.three", 'val', { force: false }); // throw's an error
    // Error: Cannot read property 'two' of undefined (at keypath 'one' of 'one.two.three')

    IN

    Returns true if keypath is "in" the obj at the keypath. Similar to "in" operator.

    • @param {any} obj - context to read keypath in
    • @param {string} keypath - bracket and/or dot notation keypath string to read from obj
    • @param {?object} opts - optional, defaults to { force: true }
    • opts.force - force specifies whether non-existant keypaths should be ignored, defaults to true
    • @returns {boolean} true if the keypath is "in" the obj, else false
    const keypathIn = require('keypather/in');
    const obj = {
      foo: {
        bar: {
          baz: 'val'
          __proto__: {
            qux: 'val'
          }
        }
      }
    };
    keypathIn(obj, "foo.bar.baz");           // true
    keypathIn(obj, "foo.bar.qux");           // true
    keypathIn(obj, "foo.bar.bing");          // false
    keypathIn(obj, "foo['bar'].baz");        // true
    keypathIn(obj, "one.two.three");         // false
     
    // Errors, force=false
    keypathIn(obj, "one.two.three", { force: false });
    // Error: Cannot read property 'two' of undefined (at keypath 'two' of 'one.two.three')
    keypathIn(obj, "foo.two.three", { force: false });
    // TypeError: Cannot use 'in' operator to search for 'three' in undefined (at 'foo.two' of 'foo.two.three')

    HAS

    Returns true if the obj has the keypath. Similar to obj.hasOwnProperty.

    • @param {any} obj - context to read keypath in
    • @param {string} keypath - bracket and/or dot notation keypath string to read from obj
    • @param {?object} opts - optional, defaults to { force: true }
    • opts.force - force specifies whether non-existant keypaths should be ignored, defaults to true
    • @returns {boolean} true if the keypath is "in" the obj, else false
    const hasKeypath = require('keypather/has');
    const obj = {
      foo: {
        bar: {
          baz: 'val'
          __proto__: {
            qux: 'val'
          }
        }
      }
    };
    hasKeypath(obj, "foo.bar.baz");           // true
    hasKeypath(obj, "foo.bar.qux");           // false
    hasKeypath(obj, "['foo']['bar']['baz']"); // true
    hasKeypath(obj, "one.two.three");         // false
     
    // Errors, force=false
    hasKeypath(obj, "one.two.three", { force: false }); // throw's an error
    // Error: Cannot read property 'two' of undefined (at keypath 'two' of 'one.two.three
    hasKeypath(obj, "foo.two.three", { force: false });
    // Error: Cannot read property 'hasOwnProperty' of undefined (hasOwnProperty('three') errored at keypath 'foo.two' of 'foo.two.three')

    FLATTEN

    Flatten an object or array into a keypath object

    • @param {any} obj - object or array to flatten
    const flatten = require('keypather/flatten');
     
    flatten({
      foo: {
        qux: 'hello'
      },
      bar: [
        1,
        {
          yolo: [1]
        }
      ]
    });
    // returns:
    // {
    //   'foo.qux': 'hello',
    //   'bar[0]': 1,
    //   'bar[1].yolo[0]': 1
    // }
     
    /* accepts a delimiter other than '.' as second arg */
     
    flatten({
      foo: {
        qux: 'hello'
      }
    }, '_');
    // returns:
    // {
    //   'foo_qux': 'hello',
    // }
     

    EXPAND

    Expand a flattened object back into an object or array

    • @param {any} obj - flattened object or array to be expanded
    const expand = require('keypather/expand');
     
    expand({
      'foo.qux': 'hello',
      'bar[0]': 1,
      'bar[1].yolo[0]': 1
    });
    // returns:
    // {
    //   foo: {
    //     qux: 'hello'
    //   },
    //   bar: [
    //     1,
    //     {
    //       yolo: [1]
    //     }
    //   ]
    // }
     
    /* expand will assume an object is an array if any of the keys are numbers */
     
    expand({
      '[0]': 1,
      '[1].yolo[0]': 1
    });
    // returns:
    // [
    //   1,
    //   {
    //     yolo: [1]
    //   }
    // ]
     
    /* accepts a delimiter other than '.' as second arg */
     
    expand({
     'foo_qux': 'hello'
    }, '_');
    // returns:
    // {
    //   foo: {
    //     qux: 'hello'
    //   }
    // }

    Changelog

    Changelog history

    License

    MIT

    Install

    npm i keypather

    DownloadsWeekly Downloads

    83,407

    Version

    3.1.0

    License

    MIT

    Unpacked Size

    121 kB

    Total Files

    56

    Last publish

    Collaborators

    • bkendall
    • dominathan
    • tjmehta