0.0.9 • Published 2 years ago

@byu-oit/cartographer v0.0.9

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
2 years ago

Introduction

This utility provides a helpful interface for converting Javascript objects into new forms.

Install

npm i @byu-oit/cartographer

Usage

Basic Usage

import { Cartographer } from '@byu-oit/cartographer'

const sourceData = { x: '1' }
const cartographer = new Cartographer(sourceData).cpy('y', 'x')
const { result } = cartographer
// expected result: { y: '1' }

Documentation

A few setup utilities are available to help fill in some of the functionality you may need for your cartographer instance. They are

  • clone
  • dictionary
  • filter
  • transformer

clone

Clones the settings from the current cartographer instance and creates a new instance. This is helpful if you want to use the same settings for multiple different mappings.

clone (source?: Dictionary, initial?: Dictionary, options?: CartographerOptions) => Cartographer

const sourceData = { ... }
const map1 = new Cartographer(sourceData, {}, {
    filters: [
        // Filter out all empty string values
        (value: unknown) => value !== ''
    ]
})

// map2 will have all the same source data, result data, filters, transformers, and dictionaries
const map2 = map1.clone()
// expected map2.filters.length = 1

dictionary

Adds a dictionary to the cartographer configuration

dictionary (name: string, dictionary: Dictionary) => this

const sourceData = { ... }
const map = new Cartographer(sourceData)

// Add a new dictionary to convert numbers from string to number
map.dictionary('numbers', {
    one: 1,
    two: 2,
    three: 3
})

filter

Adds a filter to the cartographer configuration. Filters run in insertion order. Additional filters supplied to the set, cpy, or trl methods will run before filters in the configuration.

filter (filter: Filter) => this

const sourceData = { ... }
const map = new Cartographer(sourceData)

// Adds a filter that skips setting values in the `result` object if the value is an empty string
map.filter((value: unknown) => value !== '')

transformer

Adds a transformer to the cartographer configuration. Transformers run in insertion order. Additional transformers supplied to the set, cpy, or trl methods will run before transformers in the configuration.

transformer (transformer: Transformer) => this

const sourceData = { ... }
const map = new Cartographer(sourceData)

// Adds a transformer that casts the value to a boolean setting it in the `result` object.
map.transformer((value: unknown) => value === 'true')

The remaining methods are used to map information from the source object to the target or result object.

  • get
  • set
  • cpy
  • trl
  • trf
  • fil
  • run
  • clr
  • if

get

Retrieves data from the source object based on a property path as defined in lodash.get. The default value is the third argument passed to lodash.get.

get (from: string, defValue: unknown) => unknown

const sourceData = { x: '1' }
const map = new Cartographer(sourceData)

const x = map.get('x') // returns '1'
let y = map.get('y') // returns undefined
y = map.get('y', 2) // returns 2

set

Sets a given value in the result object at the location indicated by the property path as defined in lodash.set.

set (to: string, value: unknown, options?: SetOptions) => this

const map = new Cartographer()

// Additional filters or transformers will run before the default filters and transformers.
map.set('some.property.path', '', {
    filters: [
        // See the documentation on filters (below) for this example filter definition
        isEmptyString
    ]
})
// result: { }

map.set('some.property.path', '')
// The `isEmptyString` filter only applies for the previous operation
// result: { some: { property: { path: '' } } }

Before a value is set, any transformers or filters will run in order of insertion. The order of operations is: 1. In-line transformers 2. Default transformers 3. In-line filters 4. Default filters

In-line refers to the transformers or filters that are passed in the method invocation.

Default refers to the transformers or filters added to the cartographer configuration.

Filter

A predicate function who's resulting boolean value indicates whether the value should or should not be set.

Filter = (this: Cartographer, value: unknown) => boolean

The following example will "filter out" or not set values that are empty strings.

function isEmptyString (value: unknown): boolean {
    return value === ''
}

The example above can also be written as an arrow function. However, be aware that arrow functions don't have a this context and therefore the cartographer instance members (e.g. this.get or this.set) are not available.

NOTE: The if method on the cartographer instance implements a filter that removes itself after its first invocation.

Transformer

A predicate function who's resulting value is used in place of the selected source value. When multiple transformers are used in series, the first transformer will pass along its return value and so on till the last transformer is invoked and the value is set in the result object.

Transformer = (this: Cartographer, value: unknown, ...args: unknown[]) => unknown

The following transformer maps the source value from a string to a boolean.

function ynToBoolean (value: unknown): boolean | unknown {
    if (typeof value !== 'string' || /^Y|N$/i.test(value)) {
        return undefined
    }
    return value.toUppercase() === 'Y'
}

Like filters, transformers should not be written as an arrow function if access to the cartographer this context is required.

cpy

Copies data to the result property path from the source property path. It is functionally equivalent to invoking Cartographer.prototype.get() and then passing that value into Cartographer.prototype.set().

cpy (to: string, from: string, options?: SetOptions) => this

const sourceData = { data: { holiday: '2022-01-01' } }
const map = new Cartographer(sourceData)

map.cpy('event.date', 'data.holiday', {
    transformers: [
        function stringToDate (value: unknown): Date | undefined {
            if (typeof value !== 'string' || /^\d{4}-\d{2}-\d{2}$/.test(value) {
                return undefined
            }
            return new Date(value)
        }
    ]
})
// result: { event: { date: Sat Jan 01 2022 00:00:00 GMT-0000 (GMT) } }

trl

Translates the value at the source property path (known as a word) by looking it up in the given dictionary on the cartographer instance. If the word or dictionary is invalid, the value is set to undefined by default but an error can be thrown instead by specifying { throwErr: true } in the fourth argument of the method. Additional filters and transformers in the options will run after the value has been translated. This method is functionally equivalent to adding an in-line transformer.

trl (to: string, from: string, dictionary: string, options?: SetOptions & { throwErr?: boolean }) => this

const sourceData = { first: 'one', second: 'two' }
const map = new Cartographer(sourceData)

map.trl('first', 'first', 'numbers', { throwErr: true })
// throws an error because the dictionary "numbers" is not found on the instance

map.dictionary('numbers', { one: 1 })
// adds the "numbers" dictionary
// note that the numbers dictionary does not define the word 'two'

map.trl('first', 'first', 'numbers')
// result: { first: 1 }

map.trl('second', 'second', 'numbers', { throwErr: true })
// throws an error because the word "two" is not found in the "numbers" dictionary

trf

This is an alias operation for cpy that simplifies applying inline transformations. Multiple inline transformations may be supplied as additional arguments and will be executed in the order they are written (from left to right). For more intricate copies, please refer to the cpy operation.

trf (to: string, from: string, ...transformers: Transform[]) => this

import { Cartographer } from './cartographer'

const sourceData = { x: '1', y: 2 }
const map = new Cartographer(sourceData)

const toNum = (v: unknown) => {
 if (typeof v === 'string') {
  const result = parseFloat(v)
  if (isNaN(result)) return -1
  return result
 }
 if (typeof v !== 'number') return -1
 return v
}

map
  .trf('x', 'x', toNum)
  .trf('y', 'y', toNum)
// result: { x: 1, y: 2 }

fil

This is an alias operation for cpy that simplifies applying inline filters. Multiple inline filters may be supplied as additional arguments and will be executed in the order they are written (from left to right). For more intricate copies, please refer to the cpy operation.

fil (to: string, from: string, ...filters: Filter[]) => this

import { Cartographer } from './cartographer'

const sourceData = { x: '1', y: 2, z: '1996-04-26' }
const map = new Cartographer(sourceData)

const isDateString = (v: unknown) => {
 return typeof v === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(v)
}

map
 .fil('x', 'x', isDateString)
 .fil('y', 'y', isDateString)
 .fil('z', 'z', isDateString)
// result: { z: '1996-04-26' }

run

Executes a runner function who's return value is set on the result object. Like filters and transformers, runners should not be written as an arrow function if access to the cartographer this context is required.

run (to: string, runner: Runner) => this

const sourceData = { x: '1' }
const map = new Cartographer(sourceData)

map.run('value', function () {
    return this.get('x')
})
// result: { value: '1' }

clr

Resets the result object to the given initial value (defaults to an empty object).

clr (initial?: Dictionary | null) => this

const map = new Cartographer({}, { some: 'data' })

map.clr({ other: 'data' })
// result: { other: 'data' }

map.clr()
// result: { }

Note: if null is given as the initial argument, the default is used.

if

Sets a filter that will skip the next set action (i.e. set, cpy, trl, or run) if the condition resolves to false. The condition can be a boolean expression or value, or it can be a function that resolves to a boolean value.

if (condition: boolean | Runner) => this

const map = new Cartographer()

map.if(false).set('skipped', true)
// result: { }

map.if(() => false).set('skipped', true)
// result: { }

map.if(true).set('skipped', false)
// result: { skipped: false }
0.0.9

2 years ago

0.0.3

2 years ago

0.0.4

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago