import _ from 'lodash' const toSentence = (words: string[], lastWordConnector = 'and') => { lastWordConnector = `${words.length > 2 ? ',' : ''} ${lastWordConnector}` return words.join(', ').replace(/,\s([^,]+$)/, `${lastWordConnector} $1`) } const toCamel = (s: string) => s.replace(/(_[a-z])/g, (m: string) => m.toUpperCase().replace('_', '')) const toSnake = (s: string) => (s.startsWith('_') ? `_${_.snakeCase(s)}` : _.snakeCase(s)) const actualObject = (elem: unknown): elem is Object => { return !((_.isObject(elem) && (_.isArray(elem) || _.isFunction(elem))) || !_.isObject(elem)) } // map over tree (object) and apply f to each key const walker = (f: (key: string) => string) => { const seen = new WeakMap() const convert = (elem: object): object => { if (actualObject(elem)) { return _.reduce( elem as Record, (memo: Record, obj: unknown, key: string) => { if (!seen.has(elem)) { seen.set(elem, memo) } memo[f(key)] = seen.get(obj as object) || convert(obj as object) return memo }, {} as Record ) } else if (_.isArray(elem)) { return _.map(elem, convert) } else { return elem } } return convert } const keysToCamelCase = walker(toCamel) const keysToSnakeCase = walker(k => (typeof k !== 'string' ? k : toSnake(k))) export { keysToCamelCase, keysToSnakeCase, toSentence }