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

wild-wild-utils

v6.0.0

Published

🤠 Functional utilities using object property paths with wildcards and regexps 🌵

Downloads

1,094

Readme

Node Browsers TypeScript Codecov Minified size Mastodon Medium

🤠 Functional utilities using object property paths with wildcards and regexps. 🌵

Available functional methods include:

Unlike similar libraries, object properties can be get/set using dot-delimited paths, wildcards, regexps, slices and unions. It is built on top of wild-wild-path.

Hire me

Please reach out if you're looking for a Node.js API or CLI engineer (11 years of experience). Most recently I have been Netlify Build's and Netlify Plugins' technical lead for 2.5 years. I am available for full-time remote positions.

Install

npm install wild-wild-utils

This package works in both Node.js >=18.18.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

map(target, query, mapFunction, options?)

target: Target
query: Query
mapFunction: (value) => value
options: Options?
Return value: Target

Use a mapFunction() to modify any property matching the query.

const target = { user: { firstName: 'Alice', lastName: 'Smith' } }
map(target, 'user.*', (userProp) => userProp.toLowerCase())
// { user: { firstName: 'alice', lastName: 'smith' } }

merge(target, query, value, options?)

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

Deeply merge an object value with each object property matching the query.

If one of these properties is not an object, it is overridden instead.

Any object in value can change the merge mode using a _merge property with value "deep" (default), "shallow", "set" or "delete".

Arrays can be merged using objects in value where the keys are the array indices. Items can be updated, merged, added, inserted, appended, prepended or deleted.

const target = {
  userOne: { names: ['Alice', 'Smith'], settings: { deleted: true } },
  userTwo: { names: ['John', 'Doe'], settings: { deleted: false } },
}

merge(target, '*', { age: 72, settings: { admin: true } })
// {
//   userOne: {
//     names: ['Alice', 'Smith'],
//     settings: { deleted: true, admin: true },
//     age: 72,
//   },
//   userTwo: {
//     names: ['John', 'Doe'],
//     settings: { deleted: false, admin: true },
//     age: 72,
//   },
// }

merge(target, '*', { age: 72, settings: { admin: true }, _merge: 'shallow' })
// {
//   userOne: {
//     names: [ 'Alice', 'Smith' ],
//     settings: { admin: true },
//     age: 72,
//   },
//   userTwo: {
//     names: [ 'John', 'Doe' ],
//     settings: { admin: true },
//     age: 72,
//   },
// }

merge(target, '*', { names: { 1: 'Red' } })
// {
//   userOne: {
//     names: ['Alice', 'Red'],
//     settings: { deleted: true },
//     age: 72,
//   },
//   userTwo: {
//     names: ['John', 'Red'],
//     settings: { deleted: false },
//     age: 72,
//   },
// }

push(target, query, values, options?)

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

Concatenate an array of values with each array property matching the query.

If one of these properties is not an array, it is overridden instead.

const target = {
  userOne: { firstName: 'Alice', colors: ['red'] },
  userTwo: { firstName: 'John', colors: ['blue'] },
}
push(target, '*.colors', ['yellow', 'silver'])
// {
//   userOne: { firstName: 'Alice', colors: ['red', 'yellow', 'silver'] },
//   userTwo: { firstName: 'John', colors: ['blue', 'yellow', 'silver'] },
// }

unshift(target, query, values, options?)

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

Like push() but concatenates at the beginning of each property instead of at the end.

const target = {
  userOne: { firstName: 'Alice', colors: ['red'] },
  userTwo: { firstName: 'John', colors: ['blue'] },
}
unshift(target, '*.colors', ['yellow', 'silver'])
// {
//   userOne: { firstName: 'Alice', colors: ['yellow', 'silver', 'red'] },
//   userTwo: { firstName: 'John', colors: ['yellow', 'silver', 'blue'] },
// }

find(target, query, testFunction, options?)

target: Target
query: Query
testFunction: (value) => boolean
options: Options?
Return value: any

Return the first property that matches the query and that returns true with the testFunction().

const target = {
  userOne: { firstName: 'Alice', colors: ['red'] },
  userTwo: { firstName: 'John', colors: ['blue'] },
}
find(target, '*.firstName', (firstName) => firstName !== 'John') // 'Alice'

pick(target, query, options?)

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

Keep only the properties matching the query.

const target = {
  userOne: { firstName: 'Alice', lastName: 'Smith', age: 72, admin: true },
  userTwo: { firstName: 'John', lastName: 'Doe', age: 72, admin: true },
}
pick(target, '*./Name/')
// {
//   userOne: { firstName: 'Alice', lastName: 'Smith' },
//   userTwo: { firstName: 'John', lastName: 'Doe' },
// }

include(target, query, testFunction, options?)

target: Target
query: Query
testFunction: (value) => boolean
options: Options?
Return value: Target

Keep only the properties that match the query and that return true with the testFunction().

const target = {
  userOne: { firstName: 'Alice', lastName: 'Smith', age: 72, admin: true },
  userTwo: { firstName: 'John', lastName: 'Doe', age: 72, admin: true },
}
include(target, '**', (value) => typeof value === 'string')
// {
//   userOne: { firstName: 'Alice', lastName: 'Smith' },
//   userTwo: { firstName: 'John', lastName: 'Doe' },
// }

exclude(target, query, testFunction, options?)

target: Target
query: Query
testFunction: (value) => boolean
options: Options?
Return value: Target

Remove any property that matches the query and that returns true with the testFunction().

const target = {
  userOne: { firstName: 'Alice', lastName: 'Smith', age: 72, admin: true },
  userTwo: { firstName: 'John', lastName: 'Doe', age: 72, admin: true },
}
exclude(target, '**', (value) => typeof value === 'string')
// {
//   userOne: { age: 72, admin: true },
//   userTwo: { age: 72, admin: true },
// }

flatten(target, options?)

target: Target
options: Options?
Return value: Target

Flatten deep properties to shallow properties with dot-delimited paths.

const target = { user: { firstName: 'Bob', colors: ['red', 'blue'] } }
flatten(target)
// { 'user.firstName': 'Bob', 'user.colors.0': 'red', 'user.colors.1': 'blue' }

Target

The target value must be an object or an array.

Query

The query format is documented here. Both query strings and arrays can be used.

Options

Options are optional plain objects. They are almost the same as in wild-wild-path.

mutate

Methods: map(), merge(), push(), unshift(), exclude()
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 = { colors: ['red'] }
console.log(push(target, 'colors', ['blue']))
// { colors: ['red', 'blue'] }
console.log(target)
// { colors: ['red'] }
console.log(push(target, 'colors', ['blue'], { mutate: true }))
// { colors: ['red', 'blue'] }
console.log(target)
// { colors: ['red', 'blue'] }

entries

Methods: map(), find(), include(), exclude()
Type: boolean
Default: false

By default, properties' values are:

  • Passed as argument to callbacks like mapFunction() and testFunction()
  • Returned by find()

When true, objects with the following shape are used instead:

  • value any: property's value
  • path Path: property's full path
  • missing boolean: whether the property is missing from the target
const target = { job: '', firstName: 'Alice', lastName: 'Smith' }
find(target, '*', (value) => value !== '') // 'Alice'
find(
  target,
  '*',
  (entry) => entry.value !== '' && entry.path[0] !== 'firstName',
  { entries: true },
)
// { value: 'Smith', path: ['lastName'], missing: false },

missing

Methods: map(), merge(), push(), unshift()
Type: boolean
Default: false with map(), true with merge|push|unshift()

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

const target = {}

push(target, 'colors', ['red']) // { colors: ['red'] }
push(target, 'colors', ['red'], { missing: false }) // {}

map(target, 'name', (value = 'defaultName') => value) // {}
map(target, 'name', ({ value = 'defaultName' }) => value, {
  missing: true,
  entries: true,
}) // { name: 'defaultName' }

sort

Methods: find(), pick(), include(),
flatten()
Type: boolean
Default: false

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

const target = { user: { lastName: 'Doe', firstName: 'John', age: 72 } }
flatten(target)
// { 'user.lastName': 'Doe', 'user.firstName': 'John', 'user.age': 72 }
flatten(target, { sort: true })
// { 'user.age': 72, 'user.firstName': 'John', 'user.lastName': 'Doe' }

childFirst

Methods: find()
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: { firstName: 'Alice', lastName: '' } }
const isDefined = (value) => value !== ''
find(target, 'user.**', isDefined) // { firstName: 'Alice', lastName: '' }
find(target, 'user.**', isDefined, { childFirst: true }) // 'Alice'

leaves

Methods: map(), merge(), push(), unshift(), find()
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: { settings: { firstName: 'Alice', lastName: 'Smith' } } }
merge(target, 'user user.settings', { age: 72 })
// {
//   user: {
//     settings: { firstName: 'Alice', lastName: 'Smith', age: 72 },
//     age: 72,
//   }
// }
merge(target, 'user user.settings', { age: 72 }, { leaves: true })
// {
//   user: {
//     settings: { firstName: 'Alice', lastName: 'Smith', age: 72 },
//   }
// }

roots

Methods: map(), merge(), push(), unshift(), find()
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: { settings: { firstName: 'Alice', lastName: 'Smith' } } }
merge(target, 'user user.settings', { age: 72 })
// {
//   user: {
//     settings: { firstName: 'Alice', lastName: 'Smith', age: 72 },
//     age: 72,
//   }
// }
merge(target, 'user user.settings', { age: 72 }, { roots: true })
// {
//   user: {
//     settings: { firstName: 'Alice', lastName: 'Smith' },
//     age: 72,
//   }
// }

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 = { user: { firstName: 'Bob', colors: ['red', 'blue'] } }
flatten(target)
// { 'user.firstName': 'Bob', 'user.colors.0': 'red', 'user.colors.1': 'blue' }
flatten(target, { shallowArrays: true })
// { 'user.firstName': 'Bob', 'user.colors': ['red', 'blue'] }

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' }) }
const isDefined = (value) => value !== ''
find(target, 'user.*', isDefined) // undefined
find(target, 'user.*', isDefined, { 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!