Javascript : matching object properties pattern or prefix - javascript

const obj = {
obj_abc: '',
obj_def: '',
hello_123: '',
hello_456: ''
}
If I have an object that its property has a certain pattern of prefix how can I split them into multiple arrays?
like
const arr1 = [{
obj_abc: '',
obj_def: ''
}]
const arr2 = [{
hello_123: '',
hello_456: ''
}]
I couldn't think of a way that I can partially match the properties of an object.

My version using Object.keys
const obj = {
obj_abc: 1,
obj_def: 2,
hello_123: 3,
hello_456: 4,
}
const arr1 = [];
const arr2 = [];
Object.keys(obj).forEach(key => {
if (key.match(/hello_.*/)) {
arr1.push({
[key]: obj[key]
});
} else {
arr2.push({
[key]: obj[key]
});
}
});
console.log(arr1, arr2);

You could use reduce method on Object.entries and return one object with separate properties for each similar keys. This assumes you want to match part of the key before _
const obj = {
obj_abc: true,
obj_def: true,
hello_123: true,
hello_456: true
}
const result = Object.entries(obj).reduce((r, [k, v]) => {
const [key] = k.split('_');
if (!r[key]) r[key] = {}
r[key][k] = v
return r;
}, {})
const [r1, r2] = Object.values(result)
console.log(r1)
console.log(r2)

Simplest answer using plain javascript
const a = {
obj_abc:123,
obj_def:456,
hello_123: 123,
hello_456:456
};
const b = {};
for(k in a){
const [key] = k.split('_');
if(!b[key]) {
b[key] = [];
}
b[key].push(a[k]);
}
console.log(b);

Related

How to recursively map/restructure an object?

I would like an array of objects with all object keys from a nested object. I wrote a recursive function to do this however at the point that the function is recalled it is not going through the object as expected but rather sending back an index infinitely.
let array = [];
const findKeys = (ob) => {
let id = 0;
let keys = Object.keys(ob);
for (let i = 0; i < keys.length; i++) {
let object = {
id: id,
label: keys[i],
};
array.push(object);
id ++;
findKeys(ob[keys[i]]);
}
return array;
};
let newArray = findKeys(data);
console.log(newArray);
example data structure:
const data = {a: {
b: {
c: {
foo: 'bar'
}
}
}}
You need to check to see if you have an object before you do the next recursive call. You also are resetting id so you are going to have the ids repeated (maybe you want that?) and you are using a global for the array so it can not be used more than once.
You are going to want something like:
function getKeys(obj) {
const array = [];
let id = 0;
function loop(obj) {
Object.entries(obj).forEach(entry => {
array.push({
id: ++id,
label: entry[0],
});
if(entry[1] != null && entry[1].constructor.name === "Object") {
loop(entry[1]);
}
});
}
loop(obj);
return array;
}
const obj1 = { a: 1, b: 'bar' };
console.log(getKeys(obj1));
const obj2 = { a: 1, b: { c: 'bar' } };
console.log(getKeys(obj2));
some thing like that
see also Check that value is object literal?
const data = { a: { b: { c: { foo: 'bar' } } }}
const isObject = el => (Object.prototype.toString.call(el) === '[object Object]')
const findKeys = obj =>
{
let arr = []
, id = 0
;
getKeys(obj)
return arr
function getKeys(o)
{
Object.keys(o).forEach(key =>
{
arr.push({ id:id++, label:key })
if (isObject(o[key]))
getKeys(o[key])
})
}
}
console.log( findKeys(data) )
.as-console-wrapper {max-height: 100%!important;top:0 }
Perhaps you may do like
var data = {a: {
b: {
c: {
foo: 'bar',
arr: [1,2,3,4]
}
}
}};
function getAllKeys(obj){
var keys = (typeof obj === "object") && (obj !== null) && Object.keys(obj);
return !!keys ? keys.reduce((r,k) => r.concat(getAllKeys(obj[k])),keys)
: [];
};
var res = getAllKeys(data);
console.log(JSON.stringify(res));
Here is a simple technique, using a fairly generic, depth-first, key-collecting traversal, followed by a mapping to add the indices:
const flattenKeys = (o) =>
Object (o) === o
? Object .entries (o) .flatMap (([k, v]) => [k, ...flattenKeys (v)])
: []
const getKeys = (o) =>
flattenKeys (o) .map ((label, id) => ({label, id}))
const data = {a: {b: {c: {foo: 'bar'}}}}
console .log (getKeys (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
If you wanted a breadth-first traversal it wouldn't be much harder.
This separation of key collection and index generation makes the code much simpler to my mind.

how can i recursively place my objects in this tree

I'm trying to write a function called customAdd() that forms the following tree:
let obj = []
let obj1 = {
key: "detail1Tests",
id : "94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e"
}
let obj2 = {key:"detail1Tests.detail2Tests",id:"5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838"}
let obj3 = {key:"detail1Tests.detail2Tests.detail3Tests",id:"0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"}
let result = this.customAdd(obj, obj1);
console.log(result);
let result1 = this.customAdd(result, obj2);
console.log(result1);
let result2 = this.customAdd(result1, obj3);
console.log(result2);
};
result should hold the value of :
children: {
detail1Tests: [{
id: " 94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e "
]
result1 should be equal to :
children: {
detail1Tests: [{
id: " 94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e "
children: {
detail1Tests.detail2Tests: [{
id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838"
}
]
]
and result2 should be:
children: {
detail1Tests: [{
id: " 94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e "
children: {
detail1Tests.detail2Tests: [{
id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838"
children: {
detail1Tests.detail2Tests.detail3Tests: [{
id: "0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"
}
]
}
}
]
]
and so on...
this is the function i built which is only working on the first level:
customAdd(obj , subObj){
let obj2 = {children: {[subObj.key]: [{id: subObj.id}] }}
if(obj.children){
let obj3 = obj.children;
var kyz = Object.keys(obj3);
let obj4 = obj3[kyz[0]]
this.customAdd(obj4 , subObj)
}
else {
return {...obj,...obj2};
}
}
any ideas on how to achieve this is appreciated.
Sounds a lot like you want to create something like lodash 'set' or ramda 'assocPath'
https://lodash.com/docs/4.17.15#set
https://ramdajs.com/docs/#assocPath
The Function customAdd() had some bugs with what it's trying to achieve. it needs some modifications and it becomes like so:
customAdd(obj , subObj){
if(obj.children){
let obj3 = obj.children;
var kyz = Object.keys(obj3);
let obj4 = obj3[kyz[0]]
return this.customAdd(obj4[0] , subObj)
}
else {
obj.children=obj2.children;
return ;
}
}
keeping in mind that the object passed to this function would be modified , so if we need to hold on to result, result1, result2 independently , we need to 'deep copy' the object after passing it to our function and this could be achieved through the syntax:
let result = JSON.parse(JSON.stringify(obj));
so the code becomes :
this.customAdd(obj, obj1);
var result = JSON.parse(JSON.stringify(obj));
console.log(result);
this.customAdd(obj, obj2);
var result1 = JSON.parse(JSON.stringify(obj));;
console.log(result1);
this.customAdd(obj,obj3);
var result2 = JSON.parse(JSON.stringify(obj));;
console.log(result2);
As designreact suggested, this could easily be built atop Ramda's assocPath (or probably atop the lodash equivalent.)
const customAdd = ({key, ...rest}, o) =>
assocPath (key .split ('.') .flatMap (k => ['children', k, 0]), rest, o)
let obj1 = {
key: "detail1Tests",
id : "94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e"
}
let obj2 = {
key: "detail1Tests.detail2Tests",
id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838"}
let obj3 = {
key: "detail1Tests.detail2Tests.detail3Tests",
id: "0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"
}
const results1 = customAdd (obj1, {})
const results2 = customAdd (obj2, results1)
const results3 = customAdd (obj3, results2)
console .log (results1)
console .log (results2)
console .log (results3)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js"></script>
<script> const {assocPath} = R </script>
We split the key into parts, and then each part gets a preceding "children" and a succeeding 0, so that, for instance, "detail1Tests.detail2Tests" becomes ["children", "detail1Tests", 0, "children", "detail2Tests", 0], which is the format used by Ramda's assocPath.
But there's no need to pull in Ramda just for this. It's easy enough to write our own version of assocPath, which I've done several times for other questions on StackOverflow, including in this recent answer. It is built atop another Ramda-inspired function, assoc, and together they look like this:
const assoc = (p, v, o) =>
Number .isInteger (p) && Array .isArray (o)
? [... o .slice (0, p), v, ... o .slice (p + 1)]
: {... o, [p]: v}
const assocPath = ([p, ... ps], v, o) =>
p == undefined
? o
: ps.length == 0
? assoc (p, v, o)
: assoc (p, assocPath (ps, v, o [p] || (o [p] = Number.isInteger (ps [0]) ? [] : {})), o)
However, I would suggest that you rethink your output structure. Unless you need that format for consistency with an external system, it's pretty terrible. This would seem better to me:
{
detail1Tests: {
id: "94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e",
detail2Tests: {
id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838",
detail3Tests: {
id: "0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"
}
}
}
}
and could be easily achieved with
const customAdd = ({key, ...rest}, o) =>
assocPath (key .split ('.'), rest, o)
Or, if you really want the children node:
{
children: {
detail1Tests: {
id: "94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e"
children: {
detail2Tests: {
id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838",
children: {
detail3Tests: {
id: "0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"
}
}
}
}
}
}
}
you could use
const customAdd = ({key, ...rest}, o) =>
assocPath (key .split ('.') .flatMap (k => ['children', k]), rest, o)
The extra array wrapper seems to add nothing useful.

Iterate object keys to replace the selected one with desired keys using es6 array functions

I have a requirement to replace the available keys with the desired keys in an object for which I was trying to execute below code, which later I found out to be incorrect usage of filter for desired output. hence I need help in getting the desired results using es6 array functions.
const columns = Object.keys(someArray).filter((columnName) => {
if (someCheck === "somecheck") {
if (columnName === 'MyName') {
const newcolumnName = `Pranav`;
return newcolumnName;
} else if (columnName === 'YourName') {
const newcolumnName = `Alex`;
return newcolumnName;
}
} else {
return (columnName !== 'sometingelse') ? columnName : '';
}
}
);
Here the someArray is as below:
someArray{
abc:"djfhdjf",
xyz:"ssss",
MyName:"onename",
YourName:"somename",
sometingelse:'somevalue'
}
I am expecting columns to be:
columns{
abc:"djfhdjf",
xyz:"ssss",
Pranav:"onename",
Alex:"somename",
sometingelse:'somevalue'
}
Please suggest how can I achieve the above expected output?
Note: I dont want to use function keyword in callbacks to avoid eslint errors
You could filter the wanted keys for replacement and replace the keys by using a new key and eleting the old one.
const
object = { abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue' },
replacements = { MyName: 'Pranav', YourName: 'Alex', sometingelse: '' };
Object
.keys(object)
.filter(k => k in replacements)
.forEach(k => {
object[replacements[k]] = object[k];
delete object[k];
});
console.log(object);
For generating an object, you could map new objects and assign them to a single object.
const
object = { abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue' },
replacements = { MyName: 'Pranav', YourName: 'Alex', sometingelse: '' },
result = Object.assign(...Object
.entries(object)
.map(([k, v]) => ({ [k in replacements ? replacements[k] : k]: v }))
);
console.log(result);
const obj = {
abc: 'djfhdjf',
xyz: 'ssss',
MyName: 'onename',
YourName: 'somename',
sometingelse: 'somevalue'
};
const newObj = Object.keys(obj).reduce((acc, key) => {
if (key === 'MyName') {
acc.newMyName = obj[key];
} else if (key === 'YourName') {
acc.newYourName = obj[key];
} else {
acc[key] = obj[key];
}
return acc;
}, {});
console.log('newObj = ', newObj);
Here is my approach, a bit long solution, but its on purpose so you can see how to do it simple without too much abstraction:
const someArray = {
abc:"djfhdjf",
xyz:"ssss",
MyName:"onename",
YourName:"somename",
sometingelse:'somevalue'
}
let foo = Object.keys(someArray).map(key => {
if(key === 'MyName') {
return 'Alex'
} else if(key === 'YourName') {
key = 'Pranav'
}
return key;
})
let bar = Object.entries(someArray).map((el, i) => {
el[0] = res[i];
return el;
})
let baz = r.reduce((acc, el)=>{
acc[`${el[0]}`] = el[1];
return acc;
},{})
console.log(baz);
You could use .reduce like so. It uses a similar idea that Nina proposed by using an object to hold your replacements. Here I have used the spread syntax to add the changed key to the accumulated object, along with it's associated value.
const someArray = {abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue'},
toUse = {MyName: "Pranav", YourName: "Alex"}, // define the keys you want to change and what they should change to
res = Object.keys(someArray).reduce((acc, key) =>
({...acc, [key in toUse ? toUse[key] : key]:someArray[key]})
, {});
console.log(res);
I am running a reduce on the keys of some array starting with an empty object. The ...acc spreads out all the properties in the reduced object. ...{ [keysMap[key] || key]: obj[key] } checks if the current key is present in keysMap.If it is present,it uses that key (keysMap[key]) otherwise it just uses the keys of the existing object.(|| key).Hope that makes sense
const renameKeys = (keysMap, obj) =>
Object.keys(obj).reduce(
(acc, key) => ({
...acc,
...{ [keysMap[key] || key]: obj[key] }
}),
{}
)
const columns = renameKeys({'MyName':'Pranav','YourName':'Alex'},someArray)

How to convert snake case to camelcase in my app

I have a very weird issue in my lodash codes
I have something like
data = {
'id':'123',
'employee_name': 'John',
'employee_type': 'new'
}
var newObj = _.mapValues(data, function (value, key) {
var t = _.camelCase(key);
console.log(t) -> shows employeeName and employeeType
return _.camelCase(key);
});
I was expecting my newObj will become
data = {
'id':'123',
'employeeName': 'John',
'employeeType': 'new'
}
after I ran the codes above, it still stays the same as it was like
data = {
'id':'123',
'employee_name': 'John',
'employee_type': 'new'
}
This is super weird and I'm not sure what went wrong. Can someone help me about this? Thanks a lot!
replacing snake_case or kebab-case to camelCase only for string (ES6+):
const snakeToCamel = str =>
str.toLowerCase().replace(/([-_][a-z])/g, group =>
group
.toUpperCase()
.replace('-', '')
.replace('_', '')
);
result:
console.log(snakeToCamel('TO_CAMEL')) //toCamel
console.log(snakeToCamel('to_camel')) //toCamel
console.log(snakeToCamel('TO-CAMEL')) //toCamel
console.log(snakeToCamel('to-camel')) //toCamel
Use _.mapKeys() instead of _.mapValues():
var data = {
'id': '123',
'employee_name': 'John',
'employee_type': 'new'
};
var newObj = _.mapKeys(data, (value, key) => _.camelCase(key));
console.log('newObj: ', newObj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
If you need to ignore the redundant value param, you can use _.rearg() on _.camelCase() to generate a function that takes the 2nd param (the key) instead of the 1st param (the value).
var data = {
'id': '123',
'employee_name': 'John',
'employee_type': 'new'
};
var newObj = _.mapKeys(data, _.rearg(_.camelCase, 1));
console.log('newObj: ', newObj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
You can also easily create your own function for that:
function camelCase(obj) {
var newObj = {};
for (d in obj) {
if (obj.hasOwnProperty(d)) {
newObj[d.replace(/(\_\w)/g, function(k) {
return k[1].toUpperCase();
})] = obj[d];
}
}
return newObj;
}
var data = {
'id': '123',
'employee_name': 'John',
'employee_type': 'new'
}
console.log(camelCase(data));
Here's how to do it in native Javascript...
let data = {
'id':'123',
'employee_name': 'John',
'employee_type': 'new'
}
// #1 simple function which converts a string from snake case to camel case ...
const snakeToCamel = s => s.replace(/(_\w)/g, k => k[1].toUpperCase())
// #2 create new data object with camelCase keys...
data = Object.entries(data).reduce((x,[k,v]) => (x[snakeToCamel(k)]=v) && x, {})
console.log(data)
For my use case I needed (or wanted) a function that would handle any arbitrary json object, including nested objects, arrays, etc. Came up with this, seems to be working so far:
const fromSnakeToCamel = (data) => {
if (_.isArray(data)) {
return _.map(data, fromSnakeToCamel);
}
if (_.isObject(data)) {
return _(data)
.mapKeys((v, k) => _.camelCase(k))
.mapValues((v, k) => fromSnakeToCamel(v))
.value();
}
return data;
}
Note that if it's not an array or an object, I just return the data because I only actually want to convert keys. Anyway, hope this helps someone
These are all good answers, but they did not fit what I needed. I like Ashish's answer because it handles nested objects, but what if there are underscores in the data that you want? So, here is a varient on Bambam's answer to make it recursive, because lodash can sometimes be a pain.
function toCamelCase (obj) {
let rtn = obj
if(!rtn) {
return rtn
} else if (typeof (obj) === 'object') {
if (obj instanceof Array) {
rtn = obj.map(toCamelCase)
} else {
rtn = {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = key.replace(/(_\w)/g, k => k[1].toUpperCase())
rtn[newKey] = toCamelCase(obj[key])
}
}
}
}
return rtn
}
TypeScript
As always, nobody asked for typescript version, but here it is, please don't beat me ^-^.
Without _, No RegExp
I split functions in two modules but you can keep them outside with proper naming
I put never to mark out that the type is actually correct since TS doesn't always know if it is.
You still can use _ and get code shorter but I wanted to breakdown the process.
module CaseTransform {
export type Snake = Lowercase<`${string}_${string}`>
export type Camel = Capitalize<string> | `${Capitalize<string>}${Capitalize<string>}`
export type SnakeToCamel<S extends string> = S extends `${infer Start}_${infer Rest}` ? `${Start}${Capitalize<SnakeToCamel<Rest>>}` : S
type SnakeToCamel__TEST__ = SnakeToCamel<"my_account_profile"> // myAccountProfile
export function capitalize<S extends string>(string: S): Capitalize<S> {
if (string.length === 0) return "" as never
return (string[0].toUpperCase() + string.slice(1)) as never
}
export function snakeToCamel<S extends string>(string: S): SnakeToCamel<S> {
const [start, ...rest] = string.split("_")
return (start + rest.map(capitalize)) as never
}
const snakeToCamel__TEST__ = snakeToCamel("ASD_asd_asdad_")
}
module ObjectTransform {
export function snakeToCamel<O extends object, K extends keyof O>(object: O): { [P in K as (P extends CaseTransform.Snake ? CaseTransform.SnakeToCamel<P> : P)]: O[P] } {
return Object
.entries(object)
.reduce((result, [key, value]) => ({
...result,
[CaseTransform.snakeToCamel(key)]: value
}), {}) as never
}
}
const sample = {
id: 123,
employee_name: "John",
employee_type: "new",
camelCase: "123",
PascalCase: "123"
}
const __TEST__ = ObjectTransform.snakeToCamel(sample)
Note
If you want all characters (even abbreviations) to be in lowercase, put .toLowercase() after string AND change SnakeToCamel type to
type SnakeToCamel<S extends string> = S extends `${infer Start}_${infer Rest}` ? `${Lowercase<Start>}${Capitalize<SnakeToCamel<Rest>>}` : Lowercase<S>
Easy!
Typings Result
JavaScript Playground
function capitalize(string) {
if (string.length === 0) return ""
return (string[0].toUpperCase() + string.slice(1))
}
function snakeToCamel(string){
const [start, ...rest] = string.split("_")
return (start + rest.map(capitalize).join(""))
}
const snakeToCamel__TEST__ = snakeToCamel("ASD_asd_asdad_")
console.log(snakeToCamel__TEST__)
function objectKeysSnakeToCamel(object) {
return Object
.entries(object)
.reduce((result, [key, value]) => ({
...result,
[snakeToCamel(key)]: value
}), {})
}
const sample = {
id: 123,
employee_name: "John",
employee_type: "new",
camelCase: "123",
PascalCase: "123"
}
const __TEST__ = objectKeysSnakeToCamel(sample)
console.log(__TEST__)
Here is another answer using simple for loop.
var data = {
'id': '123',
'employee_name': 'John',
'employee_type': 'new'
};
var output = {}
for (var key in data) {
output[_.camelCase(key)] = data[key];
}
Try this it will definitely work as expected.
const helpers = {};
helpers.camelize = function(str) {
return str.trim().replace(/[A-Z]+/g, (letter, index) => {
return index == 0 ? letter.toLowerCase() : '_' + letter.toLowerCase();
}).replace(/(.(\_|-|\s)+.)/g, function(subStr) {
return subStr[0]+(subStr[subStr.length-1].toUpperCase());
});
}
helpers.camelizeKeys = function(data) {
const result = {};
for (const [key, val] of Object.entries(data)) {
result[helpers.camelize(key)] = val;
}
return result;
}
helpers.camelizeNestedKeys = function(dataObj) {
return JSON.parse(JSON.stringify(dataObj).trim().replace(/("\w+":)/g, function(keys) {
return keys.replace(/[A-Z]+/g, (letter, index) => {
return index == 0 ? letter.toLowerCase() : '_' + letter.toLowerCase();
}).replace(/(.(\_|-|\s)+.)/g, function(subStr) {
return subStr[0]+(subStr[subStr.length-1].toUpperCase());
});
}));
}
const data = {
'id':'123',
'employee_name': 'John',
'employee_type': 'new'
};
const nestedData = {
'id':'123',
'employee_name': 'John',
'employee_type': 'new',
'exployee_projects': [
{"project_name": "test1", "project_year": 2004},
{"project_name": "test2", "project_year": 2004}
]
};
// Few camelize Examples
const str1 = "banana_orange_apple_mango";
const str2 = "banana-orange-apple-mango";
const str3 = "banana orange apple mango";
const str4 = "BANANA Orange APPLE-mango";
const str5 = "banana 5orange apple #mango";
const str6 = "banana__orange-_apple5-#mango";
console.log(helpers.camelize(str1));
console.log(helpers.camelize(str2));
console.log(helpers.camelize(str3));
console.log(helpers.camelize(str4));
console.log(helpers.camelize(str5));
console.log(helpers.camelize(str6));
console.log("=============================");
// camelize object keys
console.log(helpers.camelizeKeys(data));
console.log("=============================");
// camelize nested object keys
console.log(helpers.camelizeNestedKeys(nestedData));
If you want to convert the nested object, then using lodash can be a bit painful.
I tried using regex, JSON.parse & JSON.stringify
and here is the code for the same
below code returns the new object that is having camel case instead of snake case
//input
var data = {
'id': '123',
'employee_name': 'John',
'employee_type': {'new_name': 'foo'}
};
JSON.parse(JSON.stringify(data).replace(
/(_\w)\w+":/g,
match => match[1].toUpperCase() + match.substring(2)
));
{
'id': '123',
'employeeName': 'John',
'employeeType': {'newName': 'foo'}
}
Based on Abbos Tajimov's answer (and Ali's comment), we could also take advantage of the arguments passed down to the inline function.
const snakeToCamel = str => {
if (!(/[_-]/).test(str)) return str
return str.toLowerCase()
.replace(/([-_])([a-z])/g, (_match, _p1, p2) => p2.toUpperCase())
}
camelCase(str) {
return str
.toLowerCase()
.replace(/([-_][a-z])/g, (ltr) => ltr.toUpperCase())
.replace(/[^a-zA-Z]/g, '')
}
another way
_(data)
.keys()
.map(_.camelCase)
.zipObject(_.values(data))
.value()
I really like Mardok's version with nested objects, only issue is that it converts "null" to {}
here mine:
import _ from 'lodash';
export const toCamelCase: any = (obj: any) => {
let rtn = obj
if (typeof obj === 'object') {
if (obj instanceof Array) {
rtn = obj.map(toCamelCase)
}
else if (_.isEmpty(obj)) {
rtn = null
} else {
rtn = {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = key.replace(/(_\w)/g, k => k[1].toUpperCase())
rtn[newKey] = toCamelCase(obj[key])
}
}
}
}
return rtn
}
Creates camelized object recursively.
function camelCase(obj) {
const newObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
const keyCamel = key.replace(/(\_\w)/g, (match) => match[1].toUpperCase());
const isRecursive = typeof value === 'object';
newObj[keyCamel] = isRecursive ? camelCase(value) : value;
}
}
return newObj;
}
let data = {
id: '123',
employee_name: 'John',
inner: {
employee_type: 'new'
},
}
camelCase(data);
Found in typeorm repo https://github.com/typeorm/typeorm/blob/master/src/util/StringUtils.ts#L8
export function camelCase(str: string, firstCapital: boolean = false): string {
return str.replace(
/^([A-Z])|[\s-_](\w)/g,
function (match, p1, p2, offset) {
if (firstCapital === true && offset === 0) return p1
if (p2) return p2.toUpperCase()
return p1.toLowerCase()
},
)
}
Use npm json-case-handler which will allow you to do this in one line.
It can convert any nested objects
For your case, you can do this :
const jcc = require('json-case-convertor')
const snakeCasedJson = jcc.snakeCaseKeys(yourjsonData)
Just pass the value to input and the result will be camelcase:
const snakeToCamel = input =>
console.log(
input.slice(0, input.indexOf('_')).toLowerCase() +
input[input.indexOf('_') + 1].toUpperCase() +
input.slice(input.indexOf('_') + 2)
);
const inputs = [
'underscore_case',
'first_name',
'Some_Variable',
'calculate_AGE',
'delayed_departure',
'Hello_you',
'hAI_i',
];
for (let input of inputs) {
snakeToCamel(input);
}
This function will recursively convert all snake case keys in the object to camelCase. Including objects within arrays and object within objects.
const convertSnakeCaseToCamelCase = (obj) => {
let newObj = {};
if (typeof(obj) !== 'object') {
return obj;
} else if (Array.isArray(obj)) {
newObj = [];
}
for (const key in obj) {
const childObj = convertSnakeCaseToCamelCase(obj[key]);
if (Array.isArray(obj)) {
newObj.push(childObj);
} else {
const newKey = key.replace(/(\_\w)/g, (k) => k[1].toUpperCase());
newObj[newKey] = childObj;
}
}
return newObj;
};

Convert square brackets notation to object

I have an object containing preformated attribute names of a serialized HTMLFormElement (2-dimensional):
var plain = {
id: 1,
'items[A][Z]': 2,
'items[B]': false,
'items[C][][A]': 1
}
I want to convert the object by creating the respective sub object(s):
var result = {
id: 1,
items: {
A: {Z:2},
B: false,
C: [ {A:1} ]
}
}
As far as I'm aware, this is a common practise - but I can't find more ressources on the subject. How is something like that usually called and what's the best way to convert plain to result?
Edit: I've updated the examples with an Array. This seems to be related and is also supported by the body-parser of express.
You could split the path and reduce the path by walking the given object. If no object exist, create a new property with the name, Later assign the value and delete the splitted property.
var plain = { id: 1, 'items[A][Z]': 2, 'items[B]': false };
Object.keys(plain).forEach(function (k) {
var path = k.replace(/\[/g, '.').replace(/\]/g, '').split('.'),
last = path.pop();
if (path.length) {
path.reduce(function (o, p) {
return o[p] = o[p] || {};
}, plain)[last] = plain[k];
delete plain[k];
}
});
console.log(plain);
ES6
var plain = { id: 1, 'items[A][Z]': 2, 'items[B]': false };
Object.keys(plain).forEach(k => {
var path = k.replace(/\[/g, '.').replace(/\]/g, '').split('.'),
last = path.pop();
if (path.length) {
path.reduce((o, p) => o[p] = o[p] || {}, plain)[last] = plain[k];
delete plain[k];
}
});
console.log(plain);
You could use reduce() and filter() like this.
var plain = {
id: 1,
'items[A][Z]': 2,
'items[B]': false
}
var obj = {}
var result = Object.keys(plain).reduce(function(r, e) {
if (e.match(/\[(.*?)\]/gi)) {
var keys = e.split(/\[(.*?)\]/gi).filter(e => e != '');
keys.reduce(function(a, b, i) {
return (i != keys.length - 1) ? a[b] || (a[b] = {}) : a[b] = plain[e];
}, obj)
} else {
obj[e] = plain[e];
}
return r;
}, obj)
console.log(result)

Categories