• Stars
    star
    608
  • Rank 73,735 (Top 2 %)
  • Language
    JavaScript
  • License
    Apache License 2.0
  • Created over 2 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🀠 Object property paths with wildcards and regexps 🌡

wild-wild-path logo

Node Browsers TypeScript Codecov Minified size Mastodon Medium

🀠 Object property paths with wildcards and regexps. 🌡

Get/set object properties using:

Install

npm install wild-wild-path

This package works in both Node.js >=16.17.0 and browsers.

This is an ES module. It must be loaded using an import or import() statement, not require(). If TypeScript is used, it must be configured to output ES modules, not CommonJS.

API

Methods

get(target, query, options?)

target: Target
query: Query
options: Options?
Return value: any | undefined

Return the first property matching the query.

const target = { settings: { colors: ['red', 'blue'] } }

get(target, 'settings.colors.0') // 'red'
get(target, ['settings', 'colors', 0]) // 'red'

has(target, query, options?)

target: Target
query: Query
options: Options?
Return value: boolean

Return whether the query matches any property.

const target = { settings: { lastName: undefined, colors: ['red', 'blue'] } }

has(target, 'settings.firstName') // false
has(target, ['settings', 'firstName']) // false
has(target, 'settings.lastName') // true

list(target, query, options?)

target: Target
query: Query
options: Options?
Return value: any[]

Return all properties matching the query, as an array.

const target = {
  userOne: { firstName: 'John', lastName: 'Doe', age: 72 },
  userTwo: { firstName: 'Alice', colors: ['red', 'blue', 'yellow'] },
}

list(target, 'userOne.firstName userTwo.colors.0') // ['John', 'red']
list(target, [
  ['userOne', 'firstName'],
  ['userTwo', 'colors', 0],
]) // ['John', 'red']

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

list(target, 'userTwo.colors.*') // ['red', 'blue', 'yellow']
list(target, 'userTwo.colors.0:2') // ['red', 'blue']
list(target, '**.firstName') // ['John', 'Alice']
list(target, 'userOne.*', { entries: true })
// [
//   { value: 'John', path: ['userOne', 'firstName'], missing: false },
//   { value: 'Doe', path: ['userOne', 'lastName'], missing: false },
//   { value: 72, path: ['userOne', 'age'], missing: false },
// ]

iterate(target, query, options?)

target: Target
query: Query
options: Options?
Return value: Iterable<any>

Return all properties matching the query, as an iterable. This is slower than list() but uses less memory.

const target = { settings: { colors: ['red', 'blue'] } }

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

set(target, query, value, options?)

target: Target
query: Query
value: any
options: Options?
Return value: Target

Sets all properties matching the query. The return value is a deep clone unless the mutate option is true.

const target = { colors: ['red', 'blue'] }

set(target, 'colors.0', 'yellow') // ['yellow', 'blue']
set(target, ['colors', 0], 'yellow') // ['yellow', 'blue']
set(target, 'colors.-1', 'yellow') // ['red', 'yellow']
set(target, 'colors.-0', 'yellow') // ['red', 'blue', 'yellow']
set(target, 'colors.*', 'yellow') // ['yellow', 'yellow']
set({}, 'user.0.color', 'red') // { user: [{ color: 'red' }] }
set({}, 'user.0.color', 'red', { missing: false }) // {}

remove(target, query, options?)

target: Target
query: Query
options: Options?
Return value: Target

Delete all properties matching the query. The return value is a deep clone unless the mutate option is true.

const target = { user: { firstName: 'John', lastName: 'Doe', age: 72 } }

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

Functional utilities

wild-wild-utils is a separate library which provides with additional, higher-level methods: map(), merge(), push(), unshift(), find(), pick(), include(), exclude(), flatten().

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

# Deep properties of objects or arrays.
# Dots are used for array indices, not brackets.
# Symbol properties are always ignored.
user.colors.0

πŸš‚ Unions

# Unions ("or") of queries are space-delimited.
# The string must not be empty.
colors name age

⭐ Wildcards

# Shallow wildcards target all properties/items of a single object/array
user.*

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

πŸ—ΊοΈ Regexps

# Regexps match property names
user./name/

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

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

🌡 Arrays indices

# Array indices are integers
user.colors.0

# Array indices can be negative.
# -1 is the last item.
# -0 is the item after it, which can be used to append.
user.colors.-1

🏜️ Array slices

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

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

# The end index defaults to -0, i.e. the end
user.colors.0:
user.colors.:

πŸͺ¨ Escaping

# Dots, spaces and backslashes in property names must be escaped
name\\ with\\ spaces
name\\.with\\.dots
name\\\\with\\\\backslashes

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

🏨 Root and empty strings

# A leading dot can optionally be used. It is ignored.
user.colors
.user.colors

# Root value
.

# Empty string properties
user..colors

Query arrays

⛏️ Deep properties

// Deep properties of objects or arrays.
// Symbol properties are always ignored.
['user', 'colors', 0]

πŸš‚ Unions

// Unions ("or") of queries are arrays of arrays.
// There must be at least one item.
[['colors'], ['name'], ['age']]

⭐ Wildcards

// Shallow wildcards target all properties/items of a single object/array
['user', { type: 'any' }]

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

🀠 Regexps

// Regexps match property names
['user', /name/]

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

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

🌡 Arrays indices

// Array indices are integers, not strings
['user', 'colors', 0]

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

🏜️ Array slices

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

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

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

πŸͺ¨ Escaping

// Escaping is not necessary with query arrays
['name with spaces']
['name.with.dots']
['name\\with\\backslashes']
['name', '0']
['name', '/not_a_regexp/']

🏨 Root and empty strings

// Root value
[]

// Empty string properties
['user', '', 'colors']

Paths

A "path" is any query using only property names and positive array indices. This excludes negative indices, slices, wildcards, regexps and unions.

Paths are returned by the entries option.

# Path string
user.colors.0
// Path array
['user', 'colors', 0]

Conversions and comparisons

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() method, missing option and entries option can be used to distinguish those.

const target = { name: undefined }

has(target, 'name') // true
has(target, 'colors') // false

get(target, 'name') // undefined
get(target, 'colors') // undefined
get(target, 'name', { entries: true, missing: true })
// { value: undefined, path: ['name'], missing: false }
get(target, 'colors', { entries: true, missing: true })
// { value: undefined, path: ['colors'], missing: true }

list(target, '*') // [undefined]
list(target, '*', { entries: true })
// [{ value: undefined, path: ['name'], missing: false }]

Options

Options are optional plain objects.

mutate

Methods: set(), remove()
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.

const target = {}
console.log(set(target, 'name', 'Alice')) // { name: 'Alice' }
console.log(target) // {}
console.log(set(target, 'name', 'Alice', { mutate: true })) // { name: 'Alice' }
console.log(target) // { name: 'Alice' }

entries

Methods: get(), list(), iterate()
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: property's full path
  • missing boolean: whether the property is missing from the target
const target = { firstName: 'Alice', lastName: 'Smith' }
list(target, '*') // ['Alice', 'Smith']
list(target, '*', { entries: true })
// [
//   { value: 'Alice', path: ['firstName'], missing: false },
//   { value: 'Smith', path: ['lastName'], missing: false },
// ]

missing

Methods: all except has() and remove()
Type: boolean
Default: false with list|iterate(), true with set()

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

const target = {}

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

list(target, 'name') // []
list(target, 'name', { missing: true, entries: true })
// [{ value: undefined, path: ['name'], missing: true }]

sort

Methods: get(), list(), iterate()
Type: boolean
Default: false

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

const target = { lastName: 'Doe', firstName: 'John' }
list(target, '*') // ['Doe', 'John']
list(target, '*', { sort: true }) // ['John', 'Doe']

childFirst

Methods: get(), list(), iterate()
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.

const target = { user: { name: 'Alice' } }
list(target, 'user.**') // [{ name: 'Alice' }, 'Alice']
list(target, 'user.**', { childFirst: true }) // ['Alice', { name: 'Alice' }]

leaves

Methods: all except has()
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.

const target = { user: { name: 'Alice' } }
list(target, 'user.**') // [{ name: 'Alice' }, 'Alice']
list(target, 'user.**', { leaves: true }) // ['Alice']

roots

Methods: get(), list(), iterate()
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.

const target = { user: { name: 'Alice' } }
list(target, 'user.**') // [{ name: 'Alice' }, 'Alice']
list(target, 'user.**', { roots: true }) // [{ name: 'Alice' }]

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 slices.

const target = [{ name: 'Alice' }, { name: 'Bob' }]
list(target, '**')
// [
//   [{ name: 'Alice' }, { name: 'Bob' }],
//   { name: 'Alice' },
//   'Alice',
//   { name: 'Bob' },
//   'Bob',
// ]
list(target, '**', { shallowArrays: true })
// [
//   [{ name: 'Alice' }, { name: 'Bob' }],
// ]

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 property name.

const target = { user: new User({ name: 'Alice' }) }
list(target, 'user.*') // []
list(target, 'user.*', { classes: true }) // ['Alice']

inherited

Methods: all
Type: boolean
Default: false

By default, wildcards and regexps ignore properties that are either inherited or not enumerable. Those can still be matched by using their property name.

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

Related projects

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

πŸ€”

More Repositories

1

cross-platform-node-guide

πŸ“— How to write cross-platform Node.js code
JavaScript
1,331
star
2

modern-errors

Handle errors in a simple, stable, consistent way
JavaScript
1,273
star
3

nve

Run any command on specific Node.js versions
JavaScript
614
star
4

log-process-errors

Show some ❀️ to Node.js process errors
JavaScript
471
star
5

human-signals

Human-friendly process signals
JavaScript
261
star
6

autoserver

Create a full-featured REST/GraphQL API from a configuration file
JavaScript
200
star
7

safe-json-value

⛑️ JSON serialization should never fail
JavaScript
198
star
8

cross-platform-terminal-characters

All the characters that work on most terminals
JavaScript
196
star
9

unix-permissions

Swiss Army knife for Unix permissions
JavaScript
119
star
10

test-each

πŸ€– Repeat tests. Repeat tests. Repeat tests.
JavaScript
103
star
11

Notes

Technologies I've learned
sed
58
star
12

gulp-execa

Gulp.js command execution for humans
JavaScript
55
star
13

fast-cartesian

Fast cartesian product
TypeScript
53
star
14

get-bin-path

Get the current package's binary path
JavaScript
34
star
15

wild-wild-utils

🀠 Functional utilities using object property paths with wildcards and regexps 🌡
JavaScript
28
star
16

get-node

Download a specific version of Node.js
JavaScript
19
star
17

handle-cli-error

πŸ’£ Error handler for CLI applications πŸ’₯
JavaScript
15
star
18

keep-func-props

Wrap a function without changing its name and other properties
JavaScript
13
star
19

node-version-alias

Resolve Node.js version aliases like `latest`, `lts` or `erbium`
JavaScript
12
star
20

big-cartesian

Cartesian product for big inputs
TypeScript
12
star
21

eslint-config

ESLint configuration for my own projects
JavaScript
10
star
22

preferred-node-version

Get the preferred Node.js version of a project or user
JavaScript
9
star
23

all-node-versions

List all available Node.js versions
JavaScript
9
star
24

error-serializer

Convert errors to/from plain objects.
JavaScript
9
star
25

modern-errors-http

`modern-errors` plugin to create HTTP error responses.
TypeScript
8
star
26

normalize-node-version

Normalize and validate Node.js versions
JavaScript
8
star
27

cv-website

Static page with my CV website
HTML
8
star
28

truncate-json

Truncate a JSON string.
JavaScript
8
star
29

merge-error-cause

Merge an error with its `cause`
JavaScript
7
star
30

get-node-cli

Download a specific version of Node.js (CLI)
JavaScript
7
star
31

global-cache-dir

Get the global cache directory
JavaScript
6
star
32

dev-tasks

Automated development tasks for my own projects
Shell
5
star
33

modern-errors-serialize

`modern-errors` plugin to serialize/parse errors.
JavaScript
5
star
34

portuguese-conjugation-cheat-sheet

Portuguese conjugation cheat sheet
CSS
5
star
35

spyd

Complete yet simple benchmark runner
JavaScript
5
star
36

string-byte-length

Get the UTF-8 byte length of a string.
JavaScript
5
star
37

template-javascript

JavaScript library template
JavaScript
5
star
38

colors-option

Let users toggle colors
JavaScript
5
star
39

modern-errors-winston

`modern-errors` plugin for Winston.
JavaScript
5
star
40

normalize-exception

Normalize exceptions/errors
JavaScript
5
star
41

abstract-parser

Abstraction layer for JavaScript parsers
JavaScript
4
star
42

modern-errors-cli

`modern-errors` plugin to handle errors in CLI modules.
TypeScript
4
star
43

error-custom-class

Create custom error classes
JavaScript
4
star
44

fetch-node-website

Fetch releases on nodejs.org
JavaScript
4
star
45

winston-error-format

Log errors with Winston
JavaScript
4
star
46

is-json-value

Check if a value is valid JSON.
JavaScript
4
star
47

error-cause-polyfill

Polyfill `error.cause`
JavaScript
4
star
48

modern-errors-switch

`modern-errors` plugin to execute class-specific logic.
JavaScript
4
star
49

modern-errors-clean

`modern-errors` plugin to clean stack traces.
JavaScript
4
star
50

wild-wild-parser

🀠 Parser for object property paths with wildcards and regexps 🌡
JavaScript
4
star
51

precise-now

Like `performance.now()` but in nanoseconds
TypeScript
4
star
52

error-http-response

Create HTTP error responses.
JavaScript
3
star
53

test-api

[WIP] Automated API testing
JavaScript
3
star
54

modern-errors-process

`modern-errors` plugin to handle process errors.
JavaScript
3
star
55

guess-json-indent

Guess the indentation of a JSON string.
JavaScript
3
star
56

time-resolution

Find the process's time resolution
TypeScript
3
star
57

is-error-instance

Check if a value is an `Error` instance.
TypeScript
3
star
58

declarative-merge

Merge objects/arrays declaratively
JavaScript
3
star
59

template-typescript

TypeScript library template
TypeScript
3
star
60

modern-errors-bugs

`modern-errors` plugin to print where to report bugs.
JavaScript
3
star
61

set-error-class

Properly update an error's class.
JavaScript
2
star
62

dev-parser

Parse JavaScript using a terminal
JavaScript
2
star
63

ehmicky

Node.js back-end developer
2
star
64

set-error-stack

Properly update an error's stack.
JavaScript
2
star
65

set-array

Set array items declaratively
JavaScript
2
star
66

set-error-message

Properly update an error's message.
JavaScript
2
star
67

set-error-props

Properly update an error's properties
JavaScript
2
star
68

string-byte-slice

Like `string.slice()` but bytewise.
JavaScript
2
star
69

redefine-property

Better `Object.defineProperty()`
JavaScript
2
star
70

terminal-theme

🎨 Use a color theme for your code's terminal output
JavaScript
2
star
71

wrap-error-message

Properly wrap an error's message.
JavaScript
2
star
72

chalk-string

Chalk with style strings.
JavaScript
2
star
73

error-class-utils

Properly create error classes.
JavaScript
2
star
74

create-error-types

Create multiple error types.
JavaScript
1
star
75

oh-oh

This is an.
JavaScript
1
star
76

design

Logos of my projects
1
star
77

prettier-config

Prettier configuration for my own projects
JavaScript
1
star