1.1.0 • Published 17 days ago

obby v1.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
17 days ago

Obby

A consolidated suite of Typescript-friendly object manipulation tools.

There's very little original code in Obby; its purpose is to make a number of disparate object access, manipulation, and comparison utilities consistent with each other and available in a shared package for my own projects.

'Obby' is short for 'Object Path Utilities', which is a bit reductive but is still very short. Thus, it sticks.

Installation

npm install -s obby

Basic Usage

Obby uses the ts-dot-prop library to provide basic access/manipulation of object properties using dot notation. Array items can be accessed by index, and the special * index value can be used to reference all items in an array property.

  • has(input: object, path: string) returns TRUE if a property has been defined on the input object.
  • get(input: object, path: string) Returns the value of the property at the given path, or undefined if it doesn't exist. (Note: This means has() is the only way to determine whether a property exists but has an undefined value).
  • set(input: object, path: string, value: unknown) Mutates the input object, creating the referenced property or overwriting it with the given value. (Note: Setting a property to undefined doesn't actually remove the property from the object; unset() is necessary for that.)
  • unset(input: object, path: string) Mutates the input object, deleting the property at the given path.
import { has, get, set, unset } from 'obby';

const obj = {
  handle: 'Bobby',
  name: { honorific: 'Mr.', first: 'Bob', middle: 'Jones', last: 'Smith', suffix: 'III' },
  ids: [
    { type: 'drivers-license', state: 'ny', number: '12345-6789' },
    { type: 'ssn', number: '000-00-0000' }
  ],
  addresses: {
    home: { street: '1 State Street', city: 'Everyville', state: 'NY', zip: '12345' },
    work: { street: '123 Downtown', city: 'Bigsville', state: 'NY', zip: '12345' },
    vacation: { street: '100 Seaside Blvd.', city: 'Pleasantville', state: 'NY', zip: '0123456' },
  },
};

console.log(has(obj, 'name.honorific')));
// output: `true`

console.log(get(obj, 'name.last'));
// output: 'Smith'

console.log(get(obj, 'ids.[0].type'));
// output: 'drivers-license'

set(obj, 'addresses.[0].city', 'Everytown');
console.log(get(obj, 'addresses.[*].city'));
// output: ["Everytown", "Biggsville", "Pleasantville"]

unset(obj, 'name.honorific');
unset(obj, 'ids');
unset(obj, 'addresses');
console.log(obj);
// output: { handle: 'Bobby', name: { first: 'Bob', last: 'Smith', suffix: 'III' } }

Other Helpful Functions

  • Cloning, merging, and comparison
    • clone(input: any): deep clones the input value using the fast-copy library.
    • merge(...input: object[]): Merges any number of objects with the deepmerge-ts library, respecting arrays, nested object keys, etc. Properties from 'leftmost' objects will be overwritten by same-key properties from 'later' objects in the input set.
    • equals(a: any, b: any): does a deep equality check of two variables using the fast-equals library.
  • Empty value checking
    • isEmpty(input: any, options?: IsEmptyOptions): Checks whether the input value is 'empty' or not, based on configurable logic. By default null and '' empty strings are treated as empty. Empty arrays, whitespace strings, empty objects, empty Maps and Sets, false, and falsy values in general can all be treated as empty.
    • toEmpty(input: any, options?: IsEmptyOptions): Returns undefined if the input value is empty according to the specified rules, or the original input value if it is not empty.
    • toEmptyDeep(input: any, options?: IsEmptyOptions): Recursively walks an input object or array, unsetting any empty properties.
  • Grab bag
    • copy(source: object, sourcePath: string, target: object, targetPath?: string): copies the value of the property on the source object to the target property of the target object. If no targetPath is given, the sourcePath is used for the target object as well.
    • move(source: object, sourcePath: string, target: object, targetPath?: string): same as the copy() function, but the property on the source object is unset() after copying.

TODO

It might make sense to split the dot-path code, emptiness checking, and clone/merge/compare stuff into separate piles. At that point, though, one might as well just use the underlying packages. Obby is here to make all of these relatively common operations easy to use together.

Down the line, though, it might make sense consolidate object comparison and cloning — and add diffing — to ensure their treatment of various data types and handling of edge cases stays in sync. If that happens, Obby's public interface should still remain the same — that's the nice part about being a wrapper layer.