Skip to content

Repository files navigation

Objix

Objix is a delightfully convienient, high performance, zero dependency and super lightweight utility which extends the javascript standard library to sugar many common use cases for working with any objects.

The functions are all non enumerable and include copies of Object class methods and Array prototype methods applied to the values of the object as well others to delete keys, stringify, promisify, memoize, compare, split/join objects, check types, log messages and trapping/observing property assignments.

This library is highly optimised with zero copy operations where possible. The source is under 4.7kb (3.4kb minified) which allows for fast loading and easy integration without additional compilation or tree shaking. Performance in pretty much all cases is significantly faster than lodash equivalents especially when working with small objects. For example ob._map(fn) is typically over 70% faster than _.mapValues(ob, fn) under node and over 150% faster using bun. See benchmarks for sample comparisons.

Interactive docs and demos are availble on https://objix.dev/#/docs/api, where every example is runnable and editable in the page.

Getting Started - Node

  • Install:

    > npm i -save objix
  • Require:

    require('objix')
    var o = { a: 1 }._map(v => v + 1)._log()

Getting Started - Browser

<script src="https://cdn.jsdelivr.net/gh/mattaylor/objix@main/objix.min.js"></script>

<script>
  var o = { a: 1 }._map(v => v + 1)._log()
</script>

Prototype Methods

The following methods are availble to all Objects via protoype inheritence, unless overwritten by a subclass.

_map Return a copy of this with all entries mapped by a function
_flatMap FlatMap a function to all entries of an this
_values Return values of this
_create Create a new Object based on this as a prototoype
_keys Return keys of this
_entries Return [key,value] entry pairs of this
_is Check type of this
_has Check if this includes some value
_[@@iterator] Iterate through entries of this
_clean Return a copy of this without falsey entries
_pick Create a copy of this with only entries with specific keys or values that that match a filter function
_find Find keys of this which match a function or value
_assign Assign new properties to this
_extend Assign default properties to this
_same Return new object like this with properties shared with another
_diff Return new object like this with properties not shared with another
_del Remove keys from this and return this
_some Test a function against at least one entry of this
_every Test a function against all entries of this
_at Lookup value by key path
_$ Coerce this into a string with configurable formatting
_clone Clone this with configurable depths
_join Join objects together with this with array property values
_split Split this into multiple objects from array property values
_contains Check if this contains all entries from another object to a given depth.
_eq Compare key and value identity between this and other objects to a given depth
_len Return number of entres in this.
_keyBy Re-index values of this this using a given key path
_memo Memoize this as a function with configurable result cache expiration
_bind Assign a function as a method of this with optional memoization
_log Conditionally write this to the console with an optional message
_try Call a function against this and catch any exceptions
_trap Create a proxy around this to intercept property assignments
_new Create a new object from another using this as a prototype, including traps
_wait Create a Promise which resolves this after a timeout or as determined by another function
_eval Safely evaluate an expression scoped to this

Fluent Method Chaining

Most of these function return objects including those modifying this and so can be fluently chained together.

var o = { a: 0, b: 1, c: 2 }
  ._pick(v => v > 0)
  ._log('POSITIVE') // 2022-10-07T00:00 POSITIVE { b: 1, c: 2 }
  ._map(v => v + 1)
  ._log('INCREMENT') // 2022-10-07T00:00 INCREMENT { b: 2, c: 3 }

Function Aliases

All functions documented below are also callable with a '_' prefix to the function name. This can help ensure that the function is callable when overwritten by other object property assignments.

var o = { a: 1 }._len() == { a: 1 }._len() //true
var o = { a: 1 }._find(v => v) == { a: 1 }._find(v => v) //true

Simple Classes

Any object can act as a class from which new objects can be derived. All properties of this are inherited - including traps!!

var Person = { firstName: 'john', lastName: 'doe' }
  ._trap(v => new Date(v).getDate(), 'Invalid date', 'dob')
  ._bind('age', t => Math.floor((Date.now() - new Date(t.dob)) / 31536000000))
  ._bind('name', t => t.firstName + ' ' + t.lastName)

var p1 = Person._new({ firstName: 'jane' })
p1.name() // 'jane doe'
p1._try(p => (p.dob = 'foobar'), console.log) // 'Invalid date ["dob","foobar"]' - the trap rejected it
p1.dob = '10/10/2000'
p1.age() // age in years since the dob above

Module Exports

All functions listed below are also available using traditional module exports, where the first argument of the function will be the object that the function is targeting as this if called via the object O.p.

const _ = require('objix')

_.len({ a: 1 }) == { a: 1 }._len() // true
_.find({ a: 1 }, v => v) == { a: 1 }._find(v => v) //true

Testing

The unit test suite has one test file per API function under test/, and runs both on Jest under Node and directly under Bun.

npm install
npm test              # run the suite on Jest
npm run test:coverage # run with a coverage report
npm run test:watch    # re-run on change
npm run test:bun      # run the same suite under `bun test`
npm run test:legacy   # the original console.assert script (silent on success)

Coverage is enforced at 100% of statements, branches, functions and lines for objix.js.

Notes for contributors:

  • test/setup.js loads objix for every test file. Jest picks it up through setupFiles in jest.config.js; Bun ignores that and reads preload from bunfig.toml instead. Both need to stay in step.
  • Under Jest that setup also detaches Object.prototype[Symbol.iterator]. objix installs it so any object can be spread, but Jest's equality treats any object carrying an iterator as an ordered sequence, which breaks toEqual on key order. Bun's toEqual compares by keys regardless, so it keeps the iterator installed. test/iterator.test.js restores it where the behaviour is under test — read the comments in both files before changing them.
  • Bun shares one module registry and one Object.prototype across all test files, where Jest isolates each. Tests must therefore not rely on prototype state set up by another file, and must not tear down shared state that other files depend on.
  • Prefer jest.advanceTimersByTime plus awaited microtask ticks over advanceTimersByTimeAsync, which Bun does not implement.

About

Objix is a delightfully convienient, high performance, zero dependency and super lightweight utility which extends the javascript standard library to sugar many common use cases for working with any objects.

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages