I have a deeply nested structure in the javascript object without any arrays in it.
var data = {
bar: 'a',
child: {
b: 'b',
grand: {
greatgrand: {
c: 'c'
}
}
}
};
let arr = [];
const findParentGrandparent = (obj, target) => {
Object.entries(obj).forEach(child => {
if (typeof child[1] === 'object') {
findParentGrandparent(child[1]);
}
});
};
findParentGrandparent(data, 'c');
When I call the function with a target, I want to get the taget key itself, parent and grandparent.
For example, if the target is 'c', arr should become
['c', 'greatgrand', 'grand', 'child'];
if target is 'greatgrand', it should become
['greatgrand', 'grand', 'child'];
Thanks
I did it using your recursive pattern, you can change the way it handle errors also, here I throw if there is no result.
var data = {
bar: 'a',
child: {
b: 'b',
grand: {
greatgrand: {
c: 'c'
}
}
}
};
let arr = [];
const findParentGrandparent = (obj, target) => {
for (const child of Object.entries(obj)) {
if (typeof child[1] === 'object' && child[0] !== target) {
const result = findParentGrandparent(child[1], target);
return [...result, child[0]];
} else if (child[0] === target) {
return [child[0]];
}
};
throw new Error("not found"); // If it goes there the object is not found, you can throw or return a specific flag, as you wish.
};
console.log(findParentGrandparent(data, 'c'));
var data = {
bar: 'a',
child: {
b: 'b',
grand: {
greatgrand: {
c: 'c'
}
}
}
};
/**
* #param validate {boolean} = true - Pass true if need to check for existance of `target`
*/
const findParentGrandparent = (obj, target, validate = true) => {
let result = [];
for (let [key, value] of Object.entries(obj)) {
if (key === target) {
result.push(key);
break;
}
if (value.toString() === '[object Object]') {
result.push(key);
result = result.concat(findParentGrandparent(value, target, false))
}
}
if (validate && !result.includes(target)) {
return 'Not found';
}
return result;
};
let resultC = findParentGrandparent(data, 'c').reverse();
let resultGreatgrand = findParentGrandparent(data, 'greatgrand').reverse();
console.log('Result for "c":', resultC);
console.log('Result for "greatgrand":', resultGreatgrand);
You can use a recursive generator function:
function* get_vals(d, target, c = []){
for (var i of Object.keys(d)){
if (i === target){
yield [target, ...c.slice(0, 3)]
}
if (typeof d[i] === 'object'){
yield* get_vals(d[i], target, c = [i, ...c])
}
}
}
var result = get_vals(data, 'c').next().value
Output:
["c", "greatgrand", "grand", "child"]
Need to convert the array of file paths into Treeview JSON object
Array Data:
[path1/subpath1/file1.doc",
"path1/subpath1/file2.doc",
"path1/subpath2/file1.doc",
"path1/subpath2/file2.doc",
"path2/subpath1/file1.doc",
"path2/subpath1/file2.doc",
"path2/subpath2/file1.doc",
"path2/subpath2/file2.doc",
"path2/subpath2/additionalpath1/file1.doc"]
I want below object Result:
{
"title": "path1",
"childNodes" : [
{ "title":"subpath1", "childNodes":[{ "title":"file1.doc", "childNodes":[] }] },
{ "title":"subpath2", "childNodes":[{ "title":"file1.doc", "childNodes":[] }] }
]
}
I was able to convert it into an object using the below code snippet but not able to transform the way I want it
let treePath = {};
let formattedData = {};
data.forEach(path => {
let levels = path.split("/");
let file = levels.pop();
let prevLevel = treePath;
let prevProp = levels.shift();
levels.forEach(prop => {
prevLevel[prevProp] = prevLevel[prevProp] || {};
prevLevel = prevLevel[prevProp];
prevProp = prop;
});
prevLevel[prevProp] = (prevLevel[prevProp] || []).concat([file]);
});
How can i do this????
You could reduce the parts of pathes and search for same title.
const
pathes = ["path1/subpath1/file1.doc", "path1/subpath1/file2.doc", "path1/subpath2/file1.doc", "path1/subpath2/file2.doc", "path2/subpath1/file1.doc", "path2/subpath1/file2.doc", "path2/subpath2/file1.doc", "path2/subpath2/file2.doc", "path2/subpath2/additionalpath1/file1.doc"],
result = pathes.reduce((r, path) => {
path.split('/').reduce((childNodes, title) => {
let child = childNodes.find(n => n.title === title);
if (!child) childNodes.push(child = { title, childNodes: [] });
return child.childNodes;
}, r);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I need to update the object name based on the array of the string value and the last string value should be an array.
I use array.forEach loop but I don't know how to find the object inside an object if it exists and the myArray contain around 10,000 strings.
const myArray = [
'/unit/unit/225/unit-225.pdf',
'/nit/nit-dep/4.11/nit-4.11.pdf',
'/nit/nit-dep/4.12/nit-4.12.pdf',
'/org/viti/viti-engine/5.1/viti-engine-5.1.pdf',
'/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'
];
var parentObject = {}
myArray.forEach(res => {
res = res.slice(1, res.length);
var array = res.split("/");
array.forEach((e, i) => {
........ // here I am confused
});
})
final output should be
parentObject = {
'unit': {
'unit': {
'225': {
'unit-225.pdf': []
}
}
},
'nit': {
'nit-dep': {
'4.11': {
'nit-4.11.pdf': []
},
'4.12': {
'nit-4.12.pdf': []
}
}
},
'org': {
'viti': {
'viti-engine': {
'5.1': {
'viti-engine-5.1.pdf': []
}
},
'viti-spring': {
'5.2': {
'viti-engine-5.2.pdf': []
}
}
}
}
}
Once you've split by slashes, use reduce to iterate to the nested object, creating each nested property first if necessary, then assign an array to the filename property:
const myArray = [
'/unit/unit/225/unit-225.pdf',
'/nit/nit-dep/4.11/nit-4.11.pdf',
'/nit/nit-dep/4.12/nit-4.12.pdf',
'/org/viti/viti-engine/5.1/viti-engine-5.1.pdf',
'/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'
];
var parentObject = {}
myArray.forEach((str) => {
const props = str.slice(1).split('/');
const filename = props.pop();
const lastObj = props.reduce((a, prop) => {
if (!a[prop]) {
a[prop] = {};
}
return a[prop];
}, parentObject);
lastObj[filename] = [];
});
console.log(parentObject);
You could reduce the array an reduce the path as well. At the end assign the array.
const
array = ['/unit/unit/225/unit-225.pdf', '/nit/nit-dep/4.11/nit-4.11.pdf', '/nit/nit-dep/4.12/nit-4.12.pdf', '/org/viti/viti-engine/5.1/viti-engine-5.1.pdf', '/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'],
result = array.reduce((r, path) => {
var keys = path.split(/\//).slice(1),
last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, r)[last] = [];
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A slightly faster approach.
const
array = ['/unit/unit/225/unit-225.pdf', '/nit/nit-dep/4.11/nit-4.11.pdf', '/nit/nit-dep/4.12/nit-4.12.pdf', '/org/viti/viti-engine/5.1/viti-engine-5.1.pdf', '/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'],
result = {};
for (let path of array) {
let keys = path.split(/\//).slice(1),
last = keys.pop(),
temp = result;
for (let key of keys) {
temp[key] = temp[key] || {};
temp = temp[key];
}
temp[last] = [];
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could also take a recursive approach.
Just keep shifting the split-up path until you get the final assignment for each branch.
const myArray = [
'/unit/unit/225/unit-225.pdf',
'/nit/nit-dep/4.11/nit-4.11.pdf',
'/nit/nit-dep/4.12/nit-4.12.pdf',
'/org/viti/viti-engine/5.1/viti-engine-5.1.pdf',
'/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'
];
console.log(buildTree(myArray));
function buildTree(list=[]) {
return list.reduce((node, item) => buildBranches(node, item.split(/\//g).filter(x => x !== '')), {});
}
function buildBranches(node={}, rest=[]) {
let key = rest.shift();
node[key] = rest.length < 2 ? { [rest.shift()] : [] } /** or rest.shift() */ : node[key] || {};
if (rest.length > 1) buildBranches(node[key], rest);
return node;
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
I have JSON returned from an API like so:
Contacts: [{ GivenName: "Matt", FamilyName: "Berry" }]
To keep this consistent with my code style (camelCase - lower case first letter) I want to transform the array to produce the following:
contacts: [{ givenName: "Matt", familyName: "Berry" }]
What's the easiest/best way to do this? Create a new Contact object and iterate over all the contacts in the returned array?
var jsonContacts = json["Contacts"],
contacts= [];
_.each(jsonContacts , function(item){
var contact = new Contact( item.GivenName, item.FamilyName );
contacts.push(contact);
});
or can I map the original array or transform it somehow?
If you would use lodash instead of underscore, this would do:
_.mapKeys(obj, (v, k) => _.camelCase(k))
This would convert both TitleCase and snake_case to camelCase. Note that it is not recursive though.
Here's a reliable, recursive function that will properly camelCase all of a JavaScript object's properties:
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 = (origKey.charAt(0).toLowerCase() + origKey.slice(1) || origKey).toString()
value = o[origKey]
if (value instanceof Array || (value !== null && value.constructor === Object)) {
value = toCamel(value)
}
newO[newKey] = value
}
}
}
return newO
}
Test:
var obj = {
'FirstName': 'John',
'LastName': 'Smith',
'BirthDate': new Date(),
'ArrayTest': ['one', 'TWO', 3],
'ThisKey': {
'This-Sub-Key': 42
}
}
console.log(JSON.stringify(toCamel(obj)))
Output:
{
"firstName":"John",
"lastName":"Smith",
"birthDate":"2017-02-13T19:02:09.708Z",
"arrayTest": [
"one",
"TWO",
3
],
"thisKey":{
"this-Sub-Key":42
}
}
You can do this with this recursive function (with lodash and ES6):
import { camelCase } from 'lodash';
const camelizeKeys = (obj) => {
if (Array.isArray(obj)) {
return obj.map(v => camelizeKeys(v));
} else if (obj != null && obj.constructor === Object) {
return Object.keys(obj).reduce(
(result, key) => ({
...result,
[camelCase(key)]: camelizeKeys(obj[key]),
}),
{},
);
}
return obj;
};
Test:
const obj = {
'FirstName': 'John',
'LastName': 'Smith',
'BirthDate': new Date(),
'ArrayTest': ['one', 'TWO', 3],
'ThisKey': {
'This-Sub-Key': 42
}
}
console.log(JSON.stringify(camelizeKeys(obj)))
Output:
{
"firstName": "John",
"lastName": "Smith",
"birthDate": "2018-05-31T09:03:57.844Z",
"arrayTest":[
"one",
"TWO",
3
],
"thisKey":{
"thisSubKey": 42
}
}
To change a plain object's keys from snake_case to camelCase recursively try the following
(which uses Lodash):
function objectKeysToCamelCase(snake_case_object) {
var camelCaseObject = {};
_.forEach(
snake_case_object,
function(value, key) {
if (_.isPlainObject(value) || _.isArray(value)) { // checks that a value is a plain object or an array - for recursive key conversion
value = objectKeysToCamelCase(value); // recursively update keys of any values that are also objects
}
camelCaseObject[_.camelCase(key)] = value;
}
)
return camelCaseObject;
};
test in this PLUNKER
Note: also works recursively for objects within arrays
Using lodash and ES6, this will replace all keys recursively to camelcase:
const camelCaseKeys = (obj) => {
if (!_.isObject(obj)) {
return obj;
} else if (_.isArray(obj)) {
return obj.map((v) => camelCaseKeys(v));
}
return _.reduce(obj, (r, v, k) => {
return {
...r,
[_.camelCase(k)]: camelCaseKeys(v)
};
}, {});
};
Just use humps
humps.camelize('hello_world');
humps.camelizeKeys(object, options); // will work through entire object
https://www.npmjs.com/package/humps
This is a great use case for axios interceptors
Basically, define a client class and attach a before/after interceptor that converts the request/response data.
export default class Client {
get(url, data, successCB, catchCB) {
return this._perform('get', url, data, successCB, catchCB);
}
post(url, data, successCB, catchCB) {
return this._perform('post', url, data, successCB, catchCB);
}
_perform(method, url, data, successCB, catchCB) {
// https://github.com/axios/axios#interceptors
// Add a response interceptor
axios.interceptors.response.use((response) => {
response.data = toCamelCase(response.data);
return response;
}, (error) => {
error.data = toCamelCase(error.data);
return Promise.reject(error);
});
// Add a request interceptor
axios.interceptors.request.use((config) => {
config.data = toSnakeCase(config.data);
return config;
}, (error) => {
return Promise.reject(error);
});
return axios({
method: method,
url: API_URL + url,
data: data,
headers: {
'Content-Type': 'application/json',
},
}).then(successCB).catch(catchCB)
}
}
Here's a gist with a longer example using React/axios.
there's a nice npm module for this..
https://www.npmjs.com/package/camelcase-keys
npm install camelcase-keys
const camelcaseKeys = require( "camelcase-keys" );
camelcaseKeys( { Contacts: [ { GivenName: "Matt", FamilyName: "Berry" } ] }, { deep: true } );
will return...
{ contacts: [ { givenName: "Matt", familyName: "Berry" } ] }
This solution based on the plain js solution above, uses loadash and Keeps an array if passed as a parameter and Only change the Keys
function camelCaseObject(o) {
let newO, origKey, value
if (o instanceof Array) {
newO = []
for (origKey in o) {
value = o[origKey]
if (typeof value === 'object') {
value = camelCaseObject(value)
}
newO.push(value)
}
} else {
newO = {}
for (origKey in o) {
if (o.hasOwnProperty(origKey)) {
newO[_.camelCase(origKey)] = o[origKey]
}
}
}
return newO
}
// Example
const obj = [
{'my_key': 'value'},
{'Another_Key':'anotherValue'},
{'array_key':
[{'me_too':2}]
}
]
console.log(camelCaseObject(obj))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Using lodash, you can do it like this:
export const toCamelCase = obj => {
return _.reduce(obj, (result, value, key) => {
const finalValue = _.isPlainObject(value) || _.isArray(value) ? toCamelCase(value) : value;
return { ...result, [_.camelCase(key)]: finalValue };
}, {});
};
Well I took up the challenge and think I figured it out:
var firstToLower = function(str) {
return str.charAt(0).toLowerCase() + str.slice(1);
};
var firstToUpper = function(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
};
var mapToJsObject = function(o) {
var r = {};
$.map(o, function(item, index) {
r[firstToLower(index)] = o[index];
});
return r;
};
var mapFromJsObject = function(o) {
var r = {};
$.map(o, function(item, index) {
r[firstToUpper(index)] = o[index];
});
return r;
};
// Map to
var contacts = [
{
GivenName: "Matt",
FamilyName: "Berry"
},
{
GivenName: "Josh",
FamilyName: "Berry"
},
{
GivenName: "Thomas",
FamilyName: "Berry"
}
];
var mappedContacts = [];
$.map(contacts, function(item) {
var m = mapToJsObject(item);
mappedContacts.push(m);
});
alert(mappedContacts[0].givenName);
// Map from
var unmappedContacts = [];
$.map(mappedContacts, function(item) {
var m = mapFromJsObject(item);
unmappedContacts.push(m);
});
alert(unmappedContacts[0].GivenName);
Property converter (jsfiddle)
The trick is handling the objects as arrays of object properties.
Here's handy library you might wanna try:
https://www.npmjs.com/package/camelize2
You simply need to install it with npm install --save camelize2 and then
const camelize = require('camelize2')
const response = {
Contacts: [{ GivenName: "Matt", FamilyName:"Berry" }]
}
const camelizedResponse = camelize(response)
Solution similar to #brandonscript, but in more ES6-functional way:
const camelCaseString = str => (
(str.charAt(0).toLowerCase() + str.slice(1) || str).toString()
);
const objectToCamelCase = val => {
if (typeof val != 'object' || val === null) {
return val;
}
if (val instanceof Array) {
return val.map(objectToCamelCase);
}
return Object.keys(val)
.filter(prop => val.hasOwnProperty(prop))
.map(prop => ({[camelCaseString(prop)]: objectToCamelCase(val[prop])}))
.reduce((prev, current) => ({...prev, ...current}))
};
// Example:
let converted = objectToCamelCase({UserId: 1, Hobbies: [{Id: 1, Label: "Read"}], Name: "John Doe"});
console.log(converted)
I needed a generic method that accepted an array or object. This is what I'm using (I borrowed KyorCode's firstToLower() implementation):
function convertKeysToCamelCase(obj) {
if (!obj || typeof obj !== "object") return null;
if (obj instanceof Array) {
return $.map(obj, function(value) {
return convertKeysToCamelCase(value);
});
}
var newObj = {};
$.each(obj, function(key, value) {
key = key.charAt(0).toLowerCase() + key.slice(1);
if (typeof value == "object" && !(value instanceof Array)) {
value = convertKeysToCamelCase(value);
}
newObj[key] = value;
});
return newObj;
};
Example calls:
var contact = { GivenName: "Matt", FamilyName:"Berry" };
console.log(convertKeysToCamelCase(contact));
// logs: Object { givenName="Matt", familyName="Berry"}
console.log(convertKeysToCamelCase([contact]));
// logs: [Object { givenName="Matt", familyName="Berry"}]
console.log(convertKeysToCamelCase("string"));
// logs: null
console.log(contact);
// logs: Object { GivenName="Matt", FamilyName="Berry"}
Took the challenge with lodash and some es6+ features
Here is my implementation with the reduce function.
function deeplyToCamelCase(obj) {
return _.reduce(obj, (camelCaseObj, value, key) => {
const convertedDeepValue = _.isPlainObject(value) || _.isArray(value)
? deeplyToCamelCase(value)
: value;
return { ...camelCaseObj, [_.camelCase(key)] : convertedDeepValue };
}, {});
};
Use lodash ...
function isPrimitive (variable) {
return Object(variable) !== variable
}
function toCamel (variable) {
if (isPrimitive(variable)) {
return variable
}
if (_.isArray(variable)) {
return variable.map(el => toCamel(el))
}
const newObj = {}
_.forOwn(variable, (value, key) => newObj[_.camelCase(key)] = toCamel(value))
return newObj
}
This function loop recursively through the object keys and using lodash returns a new object with every field converted to camelCase. It works also with arrays, nested arrays, nested objects.
function deepCamelCase (obj) {
const c = {}
if (typeof obj !== 'object') return obj
_.mapKeys(obj, (v, k) => {
let w = {}
if (typeof v === 'object') {
if (Array.isArray(v)) {
const k = []
for (const i of v) {
k.push(deepCamelCase(i))
}
} else {
_.mapValues(v, (n, m) => {
if (Array.isArray(n)) {
const k = []
for (const i of n) {
k.push(deepCamelCase(i))
}
w[_.camelCase(m)] = k
} else {
w[_.camelCase(m)] = deepCamelCase(n)
}
})
}
} else {
w = v
}
c[_.camelCase(k)] = w
})
return c
}
Updated code using the reference from https://plnkr.co/edit/jtsRo9yU12geH7fkQ0WL?p=preview
This handles the Objects with array with objects inside it too and so on, by keeping arrays as arrays (which you can iterate over using map)
function snakeToCamelCase(snake_case_object){
var camelCaseObject;
if (isPlainObject(snake_case_object)) {
camelCaseObject = {};
}else if(isArray(snake_case_object)){
camelCaseObject = [];
}
forEach(
snake_case_object,
function(value, key) {
if (isPlainObject(value) || isArray(value)) {
value = snakeToCamelCase(value);
}
if (isPlainObject(camelCaseObject)) {
camelCaseObject[camelCase(key)] = value;
}else if(isArray(camelCaseObject)){
camelCaseObject.push(value);
}
}
)
return camelCaseObject;
}
This is my take; more readable and with less nesting than brandoncode's implementation, and with more room for handling edge cases like Date (which isn't handled, by the way) or null:
function convertPropertiesToCamelCase(instance) {
if (instance instanceof Array) {
var result = [];
for (var i = 0; i < instance.length; i++) {
result[i] = convertPropertiesToCamelCase(instance[i]);
}
return result;
}
if (typeof instance != 'object') {
return instance;
}
var result = {};
for (var key in instance) {
if (!instance.hasOwnProperty(key)) {
continue;
}
result[key.charAt(0).toLowerCase() + key.substring(1)] = convertPropertiesToCamelCase(instance[key]);
}
return result;
}
Building on goredwards answer (which didn't handle the array fields correctly)
function objectKeysToCamelCase(snake_case_object) {
let camelCaseObject = {}
_.forEach(
snake_case_object,
function(value, key) {
if (_.isPlainObject(value)) {
value = objectKeysToCamelCase(value)
} else if (_.isArray(value)) {
value = value.map(v => _.isPlainObject(v) ? objectKeysToCamelCase(v) : v)
}
camelCaseObject[_.camelCase(key)] = value
},
)
return camelCaseObject
}
here is code I found for it, not fully tested though, but worth sharing.
It is far more readable than other answers, not sure about performance.
test it http://jsfiddle.net/ms734bqn/1/
const toCamel = (s) => {
return s.replace(/([-_][a-z])/ig, ($1) => {
return $1.toUpperCase()
.replace('-', '')
.replace('_', '');
});
};
const isArray = function (a) {
return Array.isArray(a);
};
const isObject = function (o) {
return o === Object(o) && !isArray(o) && typeof o !== 'function';
};
const keysToCamel = function (o) {
if (isObject(o)) {
const n = {};
Object.keys(o)
.forEach((k) => {
n[toCamel(k)] = keysToCamel(o[k]);
});
return n;
} else if (isArray(o)) {
return o.map((i) => {
return keysToCamel(i);
});
}
return o;
};
Pure JavaScript, shoud work fine
function convertKeysToCamelCase(object) {
if(object === undefined || object === null || typeof object !== "object") {
return object;
} else {
if(Array.isArray(object)) {
return object.map(item => convertKeysToCamelCase(item));
} else {
return Object.entries(object).reduce((result, [key, value]) => {
result[key.charAt(0).toLowerCase() + key.slice(1)] = convertKeysToCamelCase(value);
return result;
}, {});
}
}
}
you can do this simply by using json-case-convertor
const jcc = require('json-case-convertor')
const jsonData = ''//you json data to convert
const camelCasedJson = jcc.camelCaseKeys(jsonData) //Convert all the keys of object to snake case
This will handle all cascaded object as well
Convert object keys to camelCase with deep.
import _ from 'lodash';
export function objectKeysToCamelCase(entity) {
if (!_.isObject(entity)) return entity;
let result;
result = _.mapKeys(entity, (value, key) => _.camelCase(key));
result = _.mapValues(result, (value) => objectKeysToCamelCase(value));
return result;
}