Skip to content

Instantly share code, notes, and snippets.

@hughgrigg
Created November 19, 2019 15:37
Show Gist options
  • Save hughgrigg/52bb416c2b53245ea10158d3c933e93d to your computer and use it in GitHub Desktop.
Save hughgrigg/52bb416c2b53245ea10158d3c933e93d to your computer and use it in GitHub Desktop.
TypeScript JSON utils
import * as _ from 'lodash'
import * as objectPath from 'object-path'
import * as stringify from 'json-stringify-safe'
/**
* Recursively replace undefined with null.
*/
export function undefinedToNull<T>(obj: T): T {
if (obj === undefined) {
return null
}
return replaceAllXWithY<undefined, null>(obj, (x) => x === undefined, () => null)
}
/**
* Recursively replace objects with useful string conversions to strings.
*/
export function allToString(obj: any) {
return replaceAllXWithY<{ toString?: () => string }, string>(
obj,
(x) => x && typeof x.toString === 'function' && !x.toString().startsWith('[object'),
(x) => x.toString()
)
}
/**
* Generic recursive replacement.
*/
export function replaceAllXWithY<X, Y>(
obj: any,
identifyX: (x: X) => boolean,
replaceWithY: (x: X) => Y
) {
if (identifyX(obj)) {
return replaceWithY(obj)
}
if (_.isArray(obj)) {
return obj.map((item) => replaceAllXWithY(item, identifyX, replaceWithY))
}
if (_.isObject(obj)) {
return Object.keys(obj).reduce((acc, key) => {
acc[key] = replaceAllXWithY(obj[key], identifyX, replaceWithY)
return acc
}, {})
}
return obj
}
/**
* List all of the possible paths through an object.
*/
export function enumeratePaths(value: any, separator: string = '.'): string[] {
let paths: string[] = []
if (value && typeof value === 'object') {
paths = Object.getOwnPropertyNames(value)
for (const key of Object.getOwnPropertyNames(value)) {
paths = [
...paths,
...enumeratePaths(value[key]).map((subKey) => `${key}${separator}${subKey}`)
]
}
}
return paths
}
/**
* Turn e.g.
* { some: { nestedThing: { key: "value" } } }
* into
* { some_nested_thing_key: "value" }
*/
export function flattenToParams<T = any>(obj: object): { [key: string]: T } {
const deCycled = JSON.parse(stringify(obj))
return enumeratePaths(deCycled, '.').reduce((flattened: { [key: string]: T }, path: string) => {
const value = objectPath.get(obj, path)
if (typeof value !== 'object') {
const flatPath = _.snakeCase(path.replace('.', '_'))
flattened[flatPath] = value
}
return flattened
}, {})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment