Bit of a challenge I need to write some javascript which will do the following:
Search through a JSON string for something that looks like this
{"code_coverage_temp":["jacoco","pmd"]}
and then replace it with this
{"jacoco":true,"pmd":true}
It must be dynamic enough to deal with the values in array changing in size
I'm currently trying to find solution myself but any help would be appreciated
{
"server":{
"ip":"111.111.111.111",
"credentials":{
"username":"fwesf",
"password":"fewfew"
}
},
"pipeline":{
"job":{
"name":"gfhd"
},
"repository":{
"name":"github",
"location":"fwqfw",
"credentials":{
"username":"fsfs",
"password":"fdsfdsf"
}
},
"build":{
"language":"java",
"build_tool":"maven"
},
"test":{
"bool":true,
"test_type":"unit",
"testing_framework":"junit5"
},
"static_code_analysis":{
"bool":true,
"code_coverage_temp":[
"pmd",
"jacoco"
]
},
"security_tools":{
"bool":true,
"security_tools_temp":[
"zap"
]
},
"sonarcube":{
"bool":true
}
}
}
added entire json im working with
You can use replace method of the String.
Since number of values in the array can change, you need to parse the matched string so that you can iterate over the array and put all the strings in the array in an object. Finally you need to stringify the object because replace method returns a string. You can parse returned string again to get the object if you need to.
Edit
Since you posted your original json, following code will replace "static_code_analysis":{"bool":true,"code_coverage_temp":["pmd","jacoco"]} with "static_code_analysis":{"bool":true,"cobertura":true,"jacoco":false,"pmd":true }
const jsonStr = '{"server":{"ip":"111.111.111.111","credentials":{"username":"fwesf","password":"fewfew"}},"pipeline":{"job":{"name":"gfhd"},"repository":{"name":"github","location":"fwqfw","credentials":{"username":"fsfs","password":"fdsfdsf"}},"build":{"language":"java","build_tool":"maven"},"test":{"bool":true,"test_type":"unit","testing_framework":"junit5"},"static_code_analysis":{"bool":true,"code_coverage_temp":["pmd","jacoco"]},"security_tools":{"bool":true,"security_tools_temp":["zap"]},"sonarcube":{"bool":true}}}';
const pattern = /"code_coverage_temp":\[("[A-z]+",?\s?)+\]/g;
let res = jsonStr.replace(pattern, match => {
const parsedStr = JSON.parse(`{${match}}`);
let obj = parsedStr.code_coverage_temp
.reduce((acc, curr) => (acc[curr] = true, acc), {});
obj = JSON.stringify(obj);
obj = obj.replace('{', '')
.replace('}', '');
return obj;
});
res = JSON.parse(res);
console.log(res);
It would be much simpler if you just parse the json and then operate on it.
const jsonStr = '{"server":{"ip":"111.111.111.111","credentials":{"username":"fwesf","password":"fewfew"}},"pipeline":{"job":{"name":"gfhd"},"repository":{"name":"github","location":"fwqfw","credentials":{"username":"fsfs","password":"fdsfdsf"}},"build":{"language":"java","build_tool":"maven"},"test":{"bool":true,"test_type":"unit","testing_framework":"junit5"},"static_code_analysis":{"bool":true,"code_coverage_temp":["pmd","jacoco"]},"security_tools":{"bool":true,"security_tools_temp":["zap"]},"sonarcube":{"bool":true}}}';
const parsedJson = JSON.parse(jsonStr);
const obj = parsedJson.pipeline.static_code_analysis;
obj.code_coverage_temp.forEach(str => obj[str] = true);
delete obj.code_coverage_temp;
console.log(parsedJson);
You can try something like this (altough it's naive method):
const test = {
a: ['b', 'c'],
d: {
e: ['d', 'f', 'g'],
h: [],
d: 'value'
}
}
function parse(obj) {
const vals = Object.values(obj)
vals.forEach(v => {
if (Array.isArray(v)) {
v.forEach((o, i) => {
if (typeof o == 'object') {
parse(o)
}
if (typeof o == 'string') {
{
v[i] = {
o: true
}
}
}
return o
})
} else if (typeof v == 'object') {
parse(v)
}
})
}
Related
I have an object like this
{
metadata: {
correlationId: 'b24e9f21-6977-4553-abc7-416f8ed2da2d',
createdDateTime: '2021-06-15T16:46:24.247Z'
}
}
and I have an array of the properties I wanna access
[metadata, correlationId]
how can I dynamically access the property on the object?
like
keys.forEach((key) => {
object[key][key2] ???
})
it needs to be dynamic since I don't know how deep we need to access the object
Here is a solution without recursion:
const myObj = {
a: {
b: {
c: "I'm the target"
}
}
}
const keys = ['a', 'b', 'c'];
let result = myObj;
for (const key of keys) {
result = result[key];
}
console.log(result);
Or with recursion:
const finder = (obj, keys, index = 0) => {
const result = obj[keys[index++]];
if (!result) {
return obj;
}
return finder(result, keys, index);
}
console.log(finder(myObj, keys));
This is pretty similar to Accessing nested JavaScript objects and arrays by string path, except with one fewer step - you already have the keys you need in the form of an array. .reduce and access the next nested value in each iteration.
const obj = {
metadata: {
correlationId: 'b24e9f21-6977-4553-abc7-416f8ed2da2d',
createdDateTime: '2021-06-15T16:46:24.247Z'
}
};
const keys = ['metadata', 'correlationId'];
const result = keys.reduce((a, key) => a[key], obj);
console.log(result);
This is my idea to solve your problem. Tell me, if is ok for you.
let x = {
metadata: {
correlationId: 'b24e9f21-6977-4553-abc7-416f8ed2da2d',
createdDateTime: '2021-06-15T16:46:24.247Z'
}
}
let fun = x => typeof x === 'string' ? console.log(x) : Object.keys(x).map( y => fun(x[y]));
fun(x);
I have a JavaScript object array with the following structure:
somedata = {
foo: {
bar: [
{
baz: [
{
someprop: 'a'
},
{
someprop: 'b'
},
{
someprop: 'c'
}
]
},
{
baz: [
{
someprop: 'd'
},
{
someprop: 'e'
},
{
someprop: 'f'
}
]
}
]
}
}
I want to extract someprop field from this JavaScript object as an array ['a', 'b', 'c', 'd', 'e', 'f']
currently, this is my code logic to extract someprop field as an array:
const result = []
somedata.foo.bar.forEach(x => {
x.baz.forEach(y => {
result.push(y.someprop)
})
})
console.log(result) // prints ["a", "b", "c", "d", "e", "f"]
i tried to make the code more reusable by creating a function:
function extractToArray(data, arr, prop) {
let result = []
data.forEach(x => {
x[arr].forEach(y => {
result.push(y[prop])
})
})
return result;
}
console.log(extractToArray(somedata.foo.bar, 'baz', 'someprop'))
But is there a more concise, elegant, cleaner way to achieve this?
Note: possible duplicate covers an array of objects, but this is regarding an array of objects of an array of objects (so a simple map solution won't work).
You can use flatMap for that:
const somedata = {foo:{bar:[{baz:[{someprop:"a"},{someprop:"b"},{someprop:"c"}]},{baz:[{someprop:"d"},{someprop:"e"},{someprop:"f"}]}]}};
const result = somedata.foo.bar.flatMap(({baz}) => baz.map(({someprop}) => someprop));
console.log(result);
Note that not every current browser supports this yet, so you might want to use a polyfill.
You could create recursive function that will find your prop on any level and return array as a result.
const somedata = {"foo":{"bar":[{"baz":[{"someprop":"a"},{"someprop":"b"},{"someprop":"c"}]},{"baz":[{"someprop":"d"},{"someprop":"e"},{"someprop":"f"}]}]}}
function get(data, prop) {
const result = [];
for (let i in data) {
if (i == prop) result.push(data[prop]);
if (typeof data[i] == 'object') result.push(...get(data[i], prop))
}
return result;
}
console.log(get(somedata, 'someprop'))
A recursive function that does it in one functional expression:
const extractToArray = (data, prop) => Object(data) !== data ? []
: Object.values(data).flatMap(v => extractToArray(v, prop))
.concat(prop in data ? data[prop] : []);
var somedata = {foo: {bar: [{baz: [{someprop: 'a'},{someprop: 'b'},{someprop: 'c'}]},{baz: [{someprop: 'd'},{someprop: 'e'},{someprop: 'f'}]}]}}
console.log(extractToArray(somedata, "someprop"));
This is reusable in the sense that it also works when the property is not always present, or not always at the same depth within the data structure.
For others with similar question, I am adding a more generic (but possibly a bit less efficient) alternative using the JSON.parse reviver parameter
var arr = [], obj = {foo:{bar:[{baz:[{someprop:"a"},{someprop:"b"},{someprop:"c"}]},{baz:[{someprop:"d"},{someprop:"e"},{someprop:"f"}]}]}}
JSON.parse(JSON.stringify(obj), (k, v) => k === 'someprop' && arr.push(v))
console.log(arr)
I have a HUGE collection and I am looking for a property by key someplace inside the collection. What is a reliable way to get a list of references or full paths to all objects containing that key/index? I use jQuery and lodash if it helps and you can forget about infinite pointer recursion, this is a pure JSON response.
fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "d");
// [o.c]
fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "e");
// [o.c.d]
fn({ 'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd');
// [o.cc,o.cc.dd]
fwiw lodash has a _.find function that will find nested objects that are two nests deep, but it seems to fail after that. (e.g. http://codepen.io/anon/pen/bnqyh)
This should do it:
function fn(obj, key) {
if (_.has(obj, key)) // or just (key in obj)
return [obj];
// elegant:
return _.flatten(_.map(obj, function(v) {
return typeof v == "object" ? fn(v, key) : [];
}), true);
// or efficient:
var res = [];
_.forEach(obj, function(v) {
if (typeof v == "object" && (v = fn(v, key)).length)
res.push.apply(res, v);
});
return res;
}
a pure JavaScript solution would look like the following:
function findNested(obj, key, memo) {
var i,
proto = Object.prototype,
ts = proto.toString,
hasOwn = proto.hasOwnProperty.bind(obj);
if ('[object Array]' !== ts.call(memo)) memo = [];
for (i in obj) {
if (hasOwn(i)) {
if (i === key) {
memo.push(obj[i]);
} else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) {
findNested(obj[i], key, memo);
}
}
}
return memo;
}
here's how you'd use this function:
findNested({'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd');
and the result would be:
[{x: 9}, {y: 9}]
this will deep search an array of objects (hay) for a value (needle) then return an array with the results...
search = function(hay, needle, accumulator) {
var accumulator = accumulator || [];
if (typeof hay == 'object') {
for (var i in hay) {
search(hay[i], needle, accumulator) == true ? accumulator.push(hay) : 1;
}
}
return new RegExp(needle).test(hay) || accumulator;
}
If you can write a recursive function in plain JS (or with combination of lodash) that will be the best one (by performance), but if you want skip recursion from your side and want to go for a simple readable code (which may not be best as per performance) then you can use lodash#cloneDeepWith for any purposes where you have to traverse a object recursively.
let findValuesDeepByKey = (obj, key, res = []) => (
_.cloneDeepWith(obj, (v,k) => {k==key && res.push(v)}) && res
)
So, the callback you passes as the 2nd argument of _.cloneDeepWith will recursively traverse all the key/value pairs recursively and all you have to do is the operation you want to do with each. the above code is just a example of your case. Here is a working example:
var object = {
prop1: 'ABC1',
prop2: 'ABC2',
prop3: {
prop4: 'ABC3',
prop5Arr: [{
prop5: 'XYZ'
},
{
prop5: 'ABC4'
},
{
prop6: {
prop6NestedArr: [{
prop1: 'XYZ Nested Arr'
},
{
propFurtherNested: {key100: '100 Value'}
}
]
}
}
]
}
}
let findValuesDeepByKey = (obj, key, res = []) => (
_.cloneDeepWith(obj, (v,k) => {k==key && res.push(v)}) && res
)
console.log(findValuesDeepByKey(object, 'prop1'));
console.log(findValuesDeepByKey(object, 'prop5'));
console.log(findValuesDeepByKey(object, 'key100'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
With Deepdash you can pickDeep and then get paths from it, or indexate (build path->value object)
var obj = { 'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}
var cherry = _.pickDeep(obj,"d");
console.log(JSON.stringify(cherry));
// {"cc":{"d":{}},"dd":{"d":{}}}
var paths = _.paths(cherry);
console.log(paths);
// ["cc.d", "dd.d"]
paths = _.paths(cherry,{pathFormat:'array'});
console.log(JSON.stringify(paths));
// [["cc","d"],["dd","d"]]
var index = _.indexate(cherry);
console.log(JSON.stringify(index));
// {"cc.d":{},"dd.d":{}}
Here is a Codepen demo
Something like this would work, converting it to an object and recursing down.
function find(jsonStr, searchkey) {
var jsObj = JSON.parse(jsonStr);
var set = [];
function fn(obj, key, path) {
for (var prop in obj) {
if (prop === key) {
set.push(path + "." + prop);
}
if (obj[prop]) {
fn(obj[prop], key, path + "." + prop);
}
}
return set;
}
fn(jsObj, searchkey, "o");
}
Fiddle: jsfiddle
In case you don't see the updated answer from #eugene, this tweak allows for passing a list of Keys to search for!
// Method that will find any "message" in the Apex errors that come back after insert attempts
// Could be a validation rule, or duplicate record, or pagemessage.. who knows!
// Use in your next error toast from a wire or imperative catch path!
// message: JSON.stringify(this.findNested(error, ['message', 'stackTrace'])),
// Testing multiple keys: this.findNested({thing: 0, list: [{message: 'm'}, {stackTrace: 'st'}], message: 'm2'}, ['message', 'stackTrace'])
findNested(obj, keys, memo) {
let i,
proto = Object.prototype,
ts = proto.toString,
hasOwn = proto.hasOwnProperty.bind(obj);
if ('[object Array]' !== ts.call(memo)) memo = [];
for (i in obj) {
if (hasOwn(i)) {
if (keys.includes(i)) {
memo.push(obj[i]);
} else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) {
this.findNested(obj[i], keys, memo);
}
}
}
return memo.length == 0 ? null : memo;
}
Here's how I did it:
function _find( obj, field, results )
{
var tokens = field.split( '.' );
// if this is an array, recursively call for each row in the array
if( obj instanceof Array )
{
obj.forEach( function( row )
{
_find( row, field, results );
} );
}
else
{
// if obj contains the field
if( obj[ tokens[ 0 ] ] !== undefined )
{
// if we're at the end of the dot path
if( tokens.length === 1 )
{
results.push( obj[ tokens[ 0 ] ] );
}
else
{
// keep going down the dot path
_find( obj[ tokens[ 0 ] ], field.substr( field.indexOf( '.' ) + 1 ), results );
}
}
}
}
Testing it with:
var obj = {
document: {
payload: {
items:[
{field1: 123},
{field1: 456}
]
}
}
};
var results = [];
_find(obj.document,'payload.items.field1', results);
console.log(results);
Outputs
[ 123, 456 ]
We use object-scan for data processing tasks. It's pretty awesome once you've wrapped your head around how to use it.
// const objectScan = require('object-scan');
const haystack = { a: { b: { c: 'd' }, e: { f: 'g' } } };
const r = objectScan(['a.*.*'], { joined: true, rtn: 'entry' })(haystack);
console.log(r);
// => [ [ 'a.e.f', 'g' ], [ 'a.b.c', 'd' ] ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
There are plenty more examples on the website.
The shortest and simplest solution:
Array.prototype.findpath = function(item,path) {
return this.find(function(f){return item==eval('f.'+path)});
}
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;
};
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)