Wild Wild Path

Object property paths with wildcards and regexps

README

wild-wild-path logo
Node Browsers TypeScript Codecov Minified size Mastodon Medium

🤠 Object property paths with wildcards and regexps. 🌵

Get/set object properties using:

- ⛏️ Dot-delimited paths:foo.bar.0.baz
- ⭐ Wildcards: `foo., *.bar`
- 🗺️ Regexps:foo./ba?/
- 🏜️ Slices:foo.0:2
- 🚂 Unions:foo bar baz

Install


  1. ``` sh
  2. npm install wild-wild-path
  3. ```

This package works in both Node.js >=14.18.0 and
It is an ES module and must be loaded using
[an import or import() statement](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c),
not require().

API


Methods


get(target, query, options?)


target: [Target](#target)\
query: [Query](#queries)\
options: [Options?](#options)\
_Return value_: any | undefined

Return the first property matching the query.

  1. ``` js
  2. const target = { settings: { colors: ['red', 'blue'] } }

  3. get(target, 'settings.colors.0') // 'red'
  4. get(target, ['settings', 'colors', 0]) // 'red'
  5. ```

has(target, query, options?)


target: [Target](#target)\
query: [Query](#queries)\
options: [Options?](#options)\
_Return value_: boolean

Return whether the query matches any property.

  1. ``` js
  2. const target = { settings: { lastName: undefined, colors: ['red', 'blue'] } }

  3. has(target, 'settings.firstName') // false
  4. has(target, ['settings', 'firstName']) // false
  5. has(target, 'settings.lastName') // true
  6. ```

list(target, query, options?)


target: [Target](#target)\
query: [Query](#queries)\
options: [Options?](#options)\
_Return value_: any[]

Return all properties matching the query, as an array.



  1. ``` js
  2. const target = {
  3.   userOne: { firstName: 'John', lastName: 'Doe', age: 72 },
  4.   userTwo: { firstName: 'Alice', colors: ['red', 'blue', 'yellow'] },
  5. }

  6. list(target, 'userOne.firstName userTwo.colors.0') // ['John', 'red']
  7. list(target, [
  8.   ['userOne', 'firstName'],
  9.   ['userTwo', 'colors', 0],
  10. ]) // ['John', 'red']

  11. list(target, 'userOne./Name/') // ['John', 'Doe']
  12. list(target, ['userOne', /Name/]) // ['John', 'Doe']

  13. list(target, 'userTwo.colors.*') // ['red', 'blue', 'yellow']
  14. list(target, 'userTwo.colors.0:2') // ['red', 'blue']
  15. list(target, '**.firstName') // ['John', 'Alice']
  16. list(target, 'userOne.*', { entries: true })
  17. // [
  18. //   { value: 'John', path: ['userOne', 'firstName'], missing: false },
  19. //   { value: 'Doe', path: ['userOne', 'lastName'], missing: false },
  20. //   { value: 72, path: ['userOne', 'age'], missing: false },
  21. // ]
  22. ```

iterate(target, query, options?)


target: [Target](#target)\
query: [Query](#queries)\
options: [Options?](#options)\
_Return value_: [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#examples_using_the_iteration_protocols)

Return all properties matching the query, as an
This is slower than [list()](#listtarget-query-options) but uses less memory.



  1. ``` js
  2. const target = { settings: { colors: ['red', 'blue'] } }

  3. for (const color of iterate(target, 'settings.colors.*')) {
  4.   console.log(color) // 'red', 'blue'
  5. }
  6. ```

set(target, query, value, options?)


target: [Target](#target)\
query: [Query](#queries)\
value: any\
options: [Options?](#options)\
_Return value_: Target

Sets all properties matching the query. The return value is a deep clone
unless the [mutate](#mutate) option is true.

  1. ``` js
  2. const target = { colors: ['red', 'blue'] }

  3. set(target, 'colors.0', 'yellow') // ['yellow', 'blue']
  4. set(target, ['colors', 0], 'yellow') // ['yellow', 'blue']
  5. set(target, 'colors.-1', 'yellow') // ['red', 'yellow']
  6. set(target, 'colors.-0', 'yellow') // ['red', 'blue', 'yellow']
  7. set(target, 'colors.*', 'yellow') // ['yellow', 'yellow']
  8. set({}, 'user.0.color', 'red') // { user: [{ color: 'red' }] }
  9. set({}, 'user.0.color', 'red', { missing: false }) // {}
  10. ```

remove(target, query, options?)


target: [Target](#target)\
query: [Query](#queries)\
options: [Options?](#options)\
_Return value_: Target

Delete all properties matching the query. The return value is a deep clone
unless the [mutate](#mutate) option is true.



  1. ``` js
  2. const target = { user: { firstName: 'John', lastName: 'Doe', age: 72 } }

  3. remove(target, 'user.lastName') // { user: { firstName: 'John', age: 72 } }
  4. remove(target, 'user./Name/') // { user: { age: 72 } }
  5. remove(target, ['user', /Name/]) // { user: { age: 72 } }
  6. ```

Functional utilities


[wild-wild-utils](https://github.com/ehmicky/wild-wild-utils) is a separate
library which provides with additional, higher-level methods:
[map()](https://github.com/ehmicky/wild-wild-utils#maptarget-query-mapfunction-options),
[merge()](https://github.com/ehmicky/wild-wild-utils#mergetarget-query-value-options),
[push()](https://github.com/ehmicky/wild-wild-utils#pushtarget-query-values-options),
[unshift()](https://github.com/ehmicky/wild-wild-utils#unshifttarget-query-values-options),
[find()](https://github.com/ehmicky/wild-wild-utils#findtarget-query-testfunction-options),
[pick()](https://github.com/ehmicky/wild-wild-utils#picktarget-query-options),
[include()](https://github.com/ehmicky/wild-wild-utils#includetarget-query-testfunction-options),
[exclude()](https://github.com/ehmicky/wild-wild-utils#excludetarget-query-testfunction-options),
[flatten()](https://github.com/ehmicky/wild-wild-utils#flattentarget-options).

Target


The target value must be an object or an array.

Queries


There are two equivalent formats for queries: strings and arrays.

- Query strings are friendlier to CLI usage, more expressive,
  and easier to serialize.
- Query arrays are friendlier to programmatic usage, and
  faster. Also, they do not require escaping, so they should be used when the
  input is dynamic or user-provided to prevent injection attacks.

Query strings


⛏️ Deep properties


  1. ``` sh
  2. # Deep properties of objects or arrays.
  3. # Dots are used for array indices, not brackets.
  4. # Symbol properties are always ignored.
  5. user.colors.0
  6. ```

🚂 Unions


  1. ``` sh
  2. # Unions ("or") of queries are space-delimited.
  3. # The string must not be empty.
  4. colors name age
  5. ```

⭐ Wildcards


  1. ``` sh
  2. # Shallow wildcards target all properties/items of a single object/array
  3. user.*

  4. # Deep wildcards target all properties/items of 0, 1 or many objects/arrays
  5. user.**
  6. **.colors
  7. ```

🗺️ Regexps


  1. ``` sh
  2. # Regexps match property names
  3. user./name/

  4. # Flags can be used, e.g. to make it case-insensitive
  5. user./name/i

  6. # ^ $ must be used to match from the beginning or until the end
  7. user./^name$/i
  8. ```

🌵 Arrays indices


  1. ``` sh
  2. # Array indices are integers
  3. user.colors.0

  4. # Array indices can be negative.
  5. # -1 is the last item.
  6. # -0 is the item after it, which can be used to append.
  7. user.colors.-1
  8. ```

🏜️ Array slices


  1. ``` sh
  2. # Array slices. Goes from the start (included) to the end index (excluded).
  3. user.colors.0:2

  4. # The start index defaults to 0, i.e. the beginning
  5. user.colors.:2

  6. # The end index defaults to -0, i.e. the end
  7. user.colors.0:
  8. user.colors.:
  9. ```

🪨 Escaping


  1. ``` sh
  2. # Dots, spaces and backslashes in property names must be escaped
  3. name\\ with\\ spaces
  4. name\\.with\\.dots
  5. name\\\\with\\\\backslashes

  6. # Ambiguous property names must be escaped with a backslash at the beginning.
  7. # This includes properties that:
  8. # - Are integers but are not array elements
  9. # - Have multiple slashes and start with one
  10. name.\\0
  11. name.\\/not_a_regexp/
  12. ```

🏨 Root and empty strings


  1. ``` sh
  2. # A leading dot can optionally be used. It is ignored.
  3. user.colors
  4. .user.colors

  5. # Root value
  6. .

  7. # Empty string properties
  8. user..colors
  9. ```

Query arrays


⛏️ Deep properties



  1. ```es6
  2. // Deep properties of objects or arrays.
  3. // Symbol properties are always ignored.
  4. ['user', 'colors', 0]
  5. ```

🚂 Unions



  1. ```es6
  2. // Unions ("or") of queries are arrays of arrays.
  3. // There must be at least one item.
  4. [['colors'], ['name'], ['age']]
  5. ```

⭐ Wildcards



  1. ```es6
  2. // Shallow wildcards target all properties/items of a single object/array
  3. ['user', { type: 'any' }]

  4. // Deep wildcards target all properties/items of 0, 1 or many objects/arrays
  5. ['user', { type: 'anyDeep' }]
  6. [{ type: 'anyDeep' }, 'colors']
  7. ```

🤠 Regexps



  1. ```es6
  2. // Regexps match property names
  3. ['user', /name/]

  4. // Flags can be used, e.g. to make it case-insensitive
  5. ['user', /name/i]

  6. // ^ $ must be used to match from the beginning or until the end
  7. ['user', /^name$/i]
  8. ```

🌵 Arrays indices



  1. ```es6
  2. // Array indices are integers, not strings
  3. ['user', 'colors', 0]

  4. // Array indices can be negative.
  5. // -1 is the last item.
  6. // -0 is the item after it, which can be used to append.
  7. ['user', 'colors', -1]
  8. ```

🏜️ Array slices



  1. ```es6
  2. // Array slices. Goes from the start (included) to the end index (excluded).
  3. ['user', 'colors', { type: 'slice', from: 0, to: 2 }]

  4. // The start index defaults to 0, i.e. the beginning
  5. ['user', 'colors', { type: 'slice', to: 2 }]

  6. // The end index defaults to -0, i.e. the end
  7. ['user', 'colors', { type: 'slice', from: 0 }]
  8. ['user', 'colors', { type: 'slice' }]
  9. ```

🪨 Escaping



  1. ```es6
  2. // Escaping is not necessary with query arrays
  3. ['name with spaces']
  4. ['name.with.dots']
  5. ['name\\with\\backslashes']
  6. ['name', '0']
  7. ['name', '/not_a_regexp/']
  8. ```

🏨 Root and empty strings



  1. ```es6
  2. // Root value
  3. []

  4. // Empty string properties
  5. ['user', '', 'colors']
  6. ```

Paths


A "path" is any query using only
property names and positive
array indices. This excludes

Paths are returned by the [entries](#entries) option.

  1. ``` sh
  2. # Path string
  3. user.colors.0
  4. ```


  1. ```es6
  2. // Path array
  3. ['user', 'colors', 0]
  4. ```

Conversions and comparisons


[wild-wild-parser](https://github.com/ehmicky/wild-wild-parser) can be used to
convert between both formats, or to compare queries.

Undefined values


Object properties with a defined key but an undefined value are not ignored.
However, object properties without any defined key are ignored. The
[has()](#hastarget-query-options) method, [missing](#missing) option and
[entries](#entries) option can be used to distinguish those.

  1. ``` js
  2. const target = { name: undefined }

  3. has(target, 'name') // true
  4. has(target, 'colors') // false

  5. get(target, 'name') // undefined
  6. get(target, 'colors') // undefined
  7. get(target, 'name', { entries: true, missing: true })
  8. // { value: undefined, path: ['name'], missing: false }
  9. get(target, 'colors', { entries: true, missing: true })
  10. // { value: undefined, path: ['colors'], missing: true }

  11. list(target, '*') // [undefined]
  12. list(target, '*', { entries: true })
  13. // [{ value: undefined, path: ['name'], missing: false }]
  14. ```

Options


Options are optional plain objects.

mutate


_Methods_: [set()](#settarget-query-value-options),
[remove()](#removetarget-query-options)\
_Type_: boolean\
_Default_: false

By default, the target is deeply cloned.\
When true, it is directly mutated instead, which is faster but has side effects.

  1. ``` js
  2. const target = {}
  3. console.log(set(target, 'name', 'Alice')) // { name: 'Alice' }
  4. console.log(target) // {}
  5. console.log(set(target, 'name', 'Alice', { mutate: true })) // { name: 'Alice' }
  6. console.log(target) // { name: 'Alice' }
  7. ```

entries


_Methods_: [get()](#gettarget-query-options),
[list()](#listtarget-query-options),
[iterate()](#iteratetarget-query-options)\
_Type_: boolean\
_Default_: false

By default, properties' values are returned.\
When true, objects with the following shape are returned instead:

- value any: property's value
- path [Path](#paths): property's full path
- missing boolean: whether the property is missing from the
  target

  1. ``` js
  2. const target = { firstName: 'Alice', lastName: 'Smith' }
  3. list(target, '*') // ['Alice', 'Smith']
  4. list(target, '*', { entries: true })
  5. // [
  6. //   { value: 'Alice', path: ['firstName'], missing: false },
  7. //   { value: 'Smith', path: ['lastName'], missing: false },
  8. // ]
  9. ```

missing


_Methods_: all except [has()](#hastarget-query-options) and
[remove()](#removetarget-query-options)\
_Type_: boolean\
_Default_: false with list|iterate(), true with set()

When false, properties not defined in the target are
ignored.

  1. ``` js
  2. const target = {}

  3. set(target, 'name', 'Alice') // { name: 'Alice' }
  4. set(target, 'name', 'Alice', { missing: false }) // {}

  5. list(target, 'name') // []
  6. list(target, 'name', { missing: true, entries: true })
  7. // [{ value: undefined, path: ['name'], missing: true }]
  8. ```

sort


_Methods_: [get()](#gettarget-query-options),
[list()](#listtarget-query-options),
[iterate()](#iteratetarget-query-options)\
_Type_: boolean\
_Default_: false

When returning sibling object properties, sort them by the lexigographic order
of their names (not values).

  1. ``` js
  2. const target = { lastName: 'Doe', firstName: 'John' }
  3. list(target, '*') // ['Doe', 'John']
  4. list(target, '*', { sort: true }) // ['John', 'Doe']
  5. ```

childFirst


_Methods_: [get()](#gettarget-query-options),
[list()](#listtarget-query-options),
[iterate()](#iteratetarget-query-options)\
_Type_: boolean\
_Default_: false

When using unions or deep wildcards, a query might
match both a property and some of its children.

This option decides whether the returned properties should be sorted from
children to parents, or the reverse.

  1. ``` js
  2. const target = { user: { name: 'Alice' } }
  3. list(target, 'user.**') // [{ name: 'Alice' }, 'Alice']
  4. list(target, 'user.**', { childFirst: true }) // ['Alice', { name: 'Alice' }]
  5. ```

leaves


_Methods_: all except [has()](#hastarget-query-options)\
_Type_: boolean\
_Default_: false

When using unions or deep wildcards, a query might
match both a property and some of its children.

When true, only leaves are matched. In other words, a matching property is
ignored if one of its children also matches.

  1. ``` js
  2. const target = { user: { name: 'Alice' } }
  3. list(target, 'user.**') // [{ name: 'Alice' }, 'Alice']
  4. list(target, 'user.**', { leaves: true }) // ['Alice']
  5. ```

roots


_Methods_: [get()](#gettarget-query-options),
[list()](#listtarget-query-options),
[iterate()](#iteratetarget-query-options)\
_Type_: boolean\
_Default_: false

When using unions or deep wildcards, a query might
match both a property and some of its children.

When true, only roots are matched. In other words, a matching property is
ignored if one of its parents also matches.

  1. ``` js
  2. const target = { user: { name: 'Alice' } }
  3. list(target, 'user.**') // [{ name: 'Alice' }, 'Alice']
  4. list(target, 'user.**', { roots: true }) // [{ name: 'Alice' }]
  5. ```

shallowArrays


_Methods_: all\
_Type_: boolean\
_Default_: false

If true, wildcards do not recurse on arrays. Array items can
still be matched by using indices or

  1. ``` js
  2. const target = [{ name: 'Alice' }, { name: 'Bob' }]
  3. list(target, '**')
  4. // [
  5. //   [{ name: 'Alice' }, { name: 'Bob' }],
  6. //   { name: 'Alice' },
  7. //   'Alice',
  8. //   { name: 'Bob' },
  9. //   'Bob',
  10. // ]
  11. list(target, '**', { shallowArrays: true })
  12. // [
  13. //   [{ name: 'Alice' }, { name: 'Bob' }],
  14. // ]
  15. ```

classes


_Methods_: all\
_Type_: boolean\
_Default_: false

Unless true, wildcards and regexps ignore
properties of objects that are not plain objects (like class instances, errors
or functions). Those can still be matched by using their

  1. ``` js
  2. const target = { user: new User({ name: 'Alice' }) }
  3. list(target, 'user.*') // []
  4. list(target, 'user.*', { classes: true }) // ['Alice']
  5. ```

inherited


_Methods_: all\
_Type_: boolean\
_Default_: false

By default, wildcards and regexps ignore
properties that are either
or
Those can still be matched by using their

When true, inherited properties are not ignored, but not enumerable ones still
are.

Related projects


- [wild-wild-utils](https://github.com/ehmicky/wild-wild-utils): functional
  utilities using wild-wild-path's object property paths
- [wild-wild-parser](https://github.com/ehmicky/wild-wild-parser): parser for
  wild-wild-path's object property paths

Support


For any question, _don't hesitate_ to submit an issue on GitHub.

Everyone is welcome regardless of personal background. We enforce a
Code of conduct in order to promote a positive and
inclusive environment.

Contributing


This project was made with ❤️. The simplest way to give back is by starring and
sharing it online.

If the documentation is unclear or has a typo, please click on the page's Edit
button (pencil icon) and suggest a correction.

If you would like to help us fix a bug or add a new feature, please check our
guidelines. Pull requests are welcome!






ehmicky
ehmicky

💻 🎨 🤔 📖
Sylvain
Sylvain

🤔