npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@4lolo/keypather

v3.1.1

Published

Get or set a deep value using a keypath string. Supports bracket and dot notation

Downloads

27

Readme

keypather-logo

keypather Build Status Coverage Status js-standard-style

Get, set, or delete a deep value using a keypath string (supports immutable operations) and more.

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

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