'Recursively convert an object fields from snake case to camelCase
I had an object like this (fields in snake_case)
const obj = {
vt_core_random: {
user_details: {
first_name: "xyz",
last_name: "abc",
groups: [
{
id: 1,
group_type: "EXT"
},
{
id: 2,
group_type: "INT"
}
],
address_type: {
city_name: "nashik",
state: {
code_name: "MH",
name: "Maharashtra"
}
}
}
}
};
I want to recursily convert its fields to camelCase, so the expected output is given below
const obj = {
vtCoreRandom: {
userDetails: {
firstName: "xyz",
lastName: "abc",
groups: [
{
id: 1,
groupType: "EXT"
},
{
id: 2,
groupType: "INT"
}
],
addressType: {
cityName: "LMN",
state: {
codeName: "KOP",
name: "PSQ"
}
}
}
}
};
I tried using mapKeys() but I just can't wrap my head around the recursive part of this. any help is highly appreciated. Also I have the ability to use lodash if it makes the process any simpler
Solution 1:[1]
You can use lodash's _.transform() to create a recursive function that iterates the keys and converts them camel case with _.camelCase(). Transform can also handle arrays, so if the iterated object (target) is an array, we don't need to change the keys.
const camelize = obj => _.transform(obj, (acc, value, key, target) => {
const camelKey = _.isArray(target) ? key : _.camelCase(key);
acc[camelKey] = _.isObject(value) ? camelize(value) : value;
});
const obj = {"vt_core_random":{"user_details":{"first_name":"xyz","last_name":"abc","groups":[{"id":1,"group_type":"EXT"},{"id":2,"group_type":"INT"}],"address_type":{"city_name":"nashik","state":{"code_name":"MH","name":"Maharashtra"}}}}};
const result = camelize(obj);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
Solution 2:[2]
For all the people using lodash I have manipulated the accepted answer since lodash already includes the utility functions like isArray isObject camelCase
So the code reduces to this. :)
function keysToCamel(obj) {
if (isPlainObject(obj)) {
const n = {};
Object.keys(obj).forEach(k => (n[camelCase(k)] = keysToCamel(obj[k])));
return n;
} else if (isArray(obj)) obj.map(i => keysToCamel(i));
return obj;
}
Solution 3:[3]
You can do this in a fairly generic fashion, writing a function which accepts an arbitrary key-transformation function and returning one which accepts an object and returns one with the same structure but with keys transformed. This is essentially no harder than writing code specifically for camelizing the keys.
Here is one approach:
const fixKeys = (fn) => (obj) => Object .fromEntries (
Object .entries (obj) .map (([k, v]) => [
fn(k),
Array .isArray (v) ? v .map (fixKeys (fn)) : typeof v == 'object' ? fixKeys (fn) (v) : v
])
)
const camelCase = (s) => s.replace(/_(.)/g, (s, c) => c.toUpperCase())
const camelizeKeys = fixKeys (camelCase)
const obj = {vt_core_random: {user_details: {first_name: "xyz", last_name: "abc", groups: [{id: 1, group_type: "EXT"}, {id: 2, group_type: "INT"}], address_type: { city_name: "nashik", state: {code_name: "MH", name: "Maharashtra"}}}}}
console .log (camelizeKeys (obj))
If Object.fromEntries is not available in your environment it's easy enough to shim or replace. Also, this is tagged "lodash", and if you're already using it, you might want to replace some of the custom functionality here with lodash function, including the camelCase function and the Array and Object tests. Or not, as this is already fairly simple.
Solution 4:[4]
For those who are using typescript. I added typings to the accepted answer on this question.
const toCamel = (str: string): string => {
return str.replace(/([_-][a-z])/gi, ($1: string) => {
return $1.toUpperCase().replace('-', '').replace('_', '');
});
};
const isArray = function (
input: Record<string, unknown> | Record<string, unknown>[] | unknown
): input is Record<string, unknown>[] {
return Array.isArray(input);
};
const isObject = function (
obj: Record<string, unknown> | Record<string, unknown>[] | unknown
): obj is Record<string, unknown> {
return (
obj === Object(obj) && !Array.isArray(obj) && typeof obj !== 'function'
);
};
const camelize = function <T>(input: T): T {
return (function recurse<
K extends Record<string, unknown> | Record<string, unknown>[] | unknown
>(input: K): K {
if (isObject(input)) {
return Object.keys(input).reduce((acc, key) => {
return Object.assign(acc, { [toCamel(key)]: recurse(input[key]) });
}, {} as K);
} else if (isArray(input)) {
return input.map((i) => recurse(i)) as K;
}
return input;
})(input);
};
Solution 5:[5]
const obj = {
vt_core_random: {
user_details: {
first_name: "xyz",
last_name: "abc",
groups: [
{
id: 1,
group_type: "EXT"
},
{
id: 2,
group_type: "INT"
}
],
address_type: {
city_name: "nashik",
state: {
code_name: "MH",
name: "Maharashtra"
}
}
}
}
};
function toCamel(o) {
var newO, origKey, newKey, value
if (o instanceof Array) {
return o.map(function(value) {
if (typeof value === "object") {
value = toCamel(value)
}
return value
})
} else {
newO = {}
for (origKey in o) {
if (o.hasOwnProperty(origKey)) {
newKey = _.camelCase(origKey)
value = o[origKey]
if (value instanceof Array || (value !== null && value.constructor === Object)) {
value = toCamel(value)
}
newO[newKey] = value
}
}
}
return newO
}
console.log(toCamel(obj)); const obj = {
vt_core_random: {
user_details: {
first_name: "xyz",
last_name: "abc",
groups: [
{
id: 1,
group_type: "EXT"
},
{
id: 2,
group_type: "INT"
}
],
address_type: {
city_name: "nashik",
state: {
code_name: "MH",
name: "Maharashtra"
}
}
}
}
};
function toCamel(o) {
var newO, origKey, newKey, value
if (o instanceof Array) {
return o.map(function(value) {
if (typeof value === "object") {
value = toCamel(value)
}
return value
})
} else {
newO = {}
for (origKey in o) {
if (o.hasOwnProperty(origKey)) {
newKey = _.camelCase(origKey)
value = o[origKey]
if (value instanceof Array || (value !== null && value.constructor === Object)) {
value = toCamel(value)
}
newO[newKey] = value
}
}
}
return newO
}
console.log(toCamel(obj));
I have used lodash in this code.
Solution 6:[6]
I used the code from Donny Verduijn, but found that Date objects were getting converted to {}.
I also needed the ability to convert from snakeToCamel and camelToSnake so I added a formatter parameter.
export const snakeToCamel = (str: string): string =>
str.replace(/([_-][a-z])/gi, ($1: string) => $1.toUpperCase().replace('-', '').replace('_', ''));
export const camelToSnake = (str: string): string =>
str.replace(/([A-Z])/g, ($1: string) => `_${$1.toLowerCase()}`);
const isArray = function (
input: Record<string, unknown> | Record<string, unknown>[] | unknown
): input is Record<string, unknown>[] {
return Array.isArray(input);
};
export const isObject = function (
obj: Record<string, unknown> | Record<string, unknown>[] | unknown
): obj is Record<string, unknown> {
return (
isValidDate(obj) === false &&
obj === Object(obj) &&
!Array.isArray(obj) &&
typeof obj !== 'function'
);
};
const isValidDate = (value: any) => value instanceof Date;
const modifyObjectKeys = function <T>(input: T, formatter: (word: string) => string): T {
return (function recurse<K extends Record<string, unknown> | Record<string, unknown>[] | unknown>(
input: K
): K {
if (isObject(input)) {
return Object.keys(input).reduce(
(acc, key) => Object.assign(acc, { [formatter(key)]: recurse(input[key]) }),
{} as K
);
} else if (isArray(input)) {
return input.map((i) => recurse(i)) as K;
}
return input;
})(input);
};
/**
*
* @param input Object to convert keys to camelCase
* @returns Object with keys converted to camelCase
*/
export const camelize = function <T>(input: T): T {
return modifyObjectKeys(input, snakeToCamel);
};
/**
*
* @param input Object to convert keys to snake_case
* @returns Object with keys converted to snake_case
*/
export const snakeify = function <T>(input: T): T {
return modifyObjectKeys(input, camelToSnake);
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | slifty |
| Solution 3 | Scott Sauyet |
| Solution 4 | Donny Verduijn |
| Solution 5 | Jeba |
| Solution 6 | Robert Hinckley |
