Skip to content

feat: add transformKeysToSnake #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#1.20.0
- feat: add transformKeysToSnake

#1.19.1
- fix: fix isPlainObject to check that the constructor is an object

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Currently supported utils:
- `snakeCase` - convert camel case and string/dash separated strings to snake case
- `sum` - calculate sum of array items
- `sumBy` - calculate sum of array items using iteratee function or string shortcut
- `transformKeysToSnake` - transform every object key to snake case with option to be recursive
- `union` - Returns the union of the given arrays
- `uniqBy` - get unique values of array by the iteratee function or property path
- `uniqWith` - Returns a new array with unique values, using a comparator function
Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function shuffle<T> (array: Array<T>): Array<T>
export function snakeCase (str: string): string
export function sum (values: Array<string>): number
export function sumBy (values: Array, iteratee: Function | string): number
export function transformKeysToSnake (obj: Object, opts?: {recursive?: boolean}) : Object
export function union (...arrays: Array[]): Array
export function uniqBy (array: Array, iteratee: Function | string): Array
export function uniqWith (array: Array, comparator: Function): Array
Expand Down
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const shuffle = require('./src/shuffle')
const snakeCase = require('./src/snakeCase')
const sum = require('./src/sum')
const sumBy = require('./src/sumBy')
const transformKeysToSnake = require('./src/transformKeysToSnake')
const union = require('./src/union')
const uniqBy = require('./src/uniqBy')
const uniqWith = require('./src/uniqWith')
Expand Down Expand Up @@ -93,6 +94,7 @@ module.exports = {
snakeCase,
sum,
sumBy,
transformKeysToSnake,
union,
uniqBy,
uniqWith,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bitfinexcom/lib-js-util-base",
"version": "1.19.1",
"version": "1.20.0",
"description": "general utils",
"main": "index.js",
"scripts": {
Expand Down
31 changes: 31 additions & 0 deletions src/transformKeysToSnake.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict'

const isPlainObject = require('./isPlainObject')
const snakeCase = require('./snakeCase')

/**
* Transform object keys to snake case
* e.g. { fooBar: 1 } => { foo_bar: 1 }
* @param {Object} obj
* @param {Object} [opts]
* @param {boolean} [opts.recursive]
* @returns {Object}
*/
const transformKeysToSnake = (obj, opts) => {
if (Array.isArray(obj)) {
return obj.map(item => transformKeysToSnake(item, opts))
}

if (!isPlainObject(obj)) {
return obj
}

return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [
snakeCase(k),
opts?.recursive ? transformKeysToSnake(v, opts) : v
])
)
}

module.exports = transformKeysToSnake
102 changes: 102 additions & 0 deletions test/transformKeystoSnake.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
'use strict'

/* eslint-env mocha */

const assert = require('assert')
const { transformKeysToSnake } = require('../index')
const { itEach } = require('mocha-it-each')

describe('transformKeysToSnake', () => {
describe('non recursive (default)', () => {
itEach('should return input when it is not an object', [new Date(), null, false], (input) => {
assert.equal(transformKeysToSnake(input), input)
})

it('should return input with snake case props recursively', () => {
const input = {
camelProp: 'true',
nestedProp: {
first_name: 'john',
lastName: 'doe',
tags: ['keepCamel']
},
someObjs: [
{ someProp: 'value' },
{ foo_bar: 'value', foo: 'value' }
]
}
const output = {
camel_prop: 'true',
nested_prop: {
first_name: 'john',
lastName: 'doe',
tags: ['keepCamel']
},
some_objs: [
{ someProp: 'value' },
{ foo_bar: 'value', foo: 'value' }
]
}
assert.deepEqual(transformKeysToSnake(input), output)
})

it('should work with array mix of object and nested arrays', () => {
const input = {
mixedArray: [123, { someKey: 'value' }, [{ nestedArrayItem: 'test' }]]
}
const output = {
mixed_array: [123, { someKey: 'value' }, [{ nestedArrayItem: 'test' }]]
}
assert.deepEqual(transformKeysToSnake(input), output)
})
})
describe('recursive', () => {
itEach('should return input when it is not an object', [new Date(), null, false], (input) => {
assert.equal(transformKeysToSnake(input, { recursive: true }), input)
})

it('should return input with snake case props recursively', () => {
const input = {
camelProp: 'true',
nestedProp: {
first_name: 'john',
lastName: 'doe',
level2: {
fooBar: 'x'
},
tags: ['keepCamel']
},
someObjs: [
{ someProp: 'value' },
{ foo_bar: 'value', foo: 'value' }
]
}
const output = {
camel_prop: 'true',
nested_prop: {
first_name: 'john',
last_name: 'doe',
level2: {
foo_bar: 'x'
},
tags: ['keepCamel']
},
some_objs: [
{ some_prop: 'value' },
{ foo_bar: 'value', foo: 'value' }
]
}
assert.deepEqual(transformKeysToSnake(input, { recursive: true }), output)
})

it('should work with array mix of object and nested arrays', () => {
const input = {
mixedArray: [123, { someKey: 'value' }, [{ nestedArrayItem: 'test' }]]
}
const output = {
mixed_array: [123, { some_key: 'value' }, [{ nested_array_item: 'test' }]]
}
assert.deepEqual(transformKeysToSnake(input, { recursive: true }), output)
})
})
})