@byu-oit/cartographer
v0.0.9
Published
The art or technique of making maps
Downloads
554
Keywords
Readme
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:
- In-line transformers
- Default transformers
- In-line filters
- 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 }