I want the difference in such a way that the I don't return the entire nested object if any of the values is different.
I have seen solutions online and they all return the entire nested objects and it doesn't work if only 1 key-value pair is changed. i don't want to show the difference as a complete nested object. it should be easier for any user to read.
for eg:
const A = {
position: 2,
attributes: [{
code: 123,
name: "xyz",
params: {
label: "hehe",
units: "currency"
}
}],
code: 1
}
const B = {
position: 3,
attributes: [{
code: 123,
name: "xyzr",
params: {
label: "heh",
units: "currency"
}
}],
code: 1
}
I want the output to be like this:
difference: {
position: {
current: 2,
previous: 3
},
attributes: {
current : [{ name: "xyz", params: { label: "hehe" } }],
previous: [{ name: "xyzr", params: {label: "heh"}}]
}
}
The code that I tried:
const compareEditedChanges = (A: any, B: any) => {
const allKeys = _.union(_.keys(A), _.keys(B));
try {
setDifference(
_.reduce(
allKeys,
(result: any, key) => {
if (!_.isEqual(A?.[key], B?.[key])) {
result[key] = {
current: A[key],
previous: B[key]
};
}
return result;
},
{}
)
);
} catch (err) {
console.error(err);
}
return difference;
};
After giving it a lot of thought to the code, I came with my own solution for a deeply nested objects comparison and listing out the differences in an object with keys as current and previous.
I didn't use any inbuilt libraries and wrote the code with simple for loop, recursion and map
const compareEditedChanges = (
previousState,
currentState
) => {
const result = [];
for (const key in currentState) {
// if value is string or number or boolean
if (
typeof currentState[key] === 'string' ||
typeof currentState[key] === 'number' ||
typeof currentState[key] === 'boolean'
) {
if (String(currentState[key]) !== String(previousState[key])) {
result.push({
[key]: {
current: currentState[key],
previous: previousState[key]
}
});
}
}
// if an array
if (
Array.isArray(currentState[key]) ||
Array.isArray(previousState[key])
) {
console.log(currentState[key])
if (currentState[key].length > 0 || previousState[key].length > 0) {
currentState[key].map((value, index) => {
// check for array of string or number or boolean
if (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean'
) {
if (
JSON.stringify(currentState[key]) !==
JSON.stringify(previousState[key])
) {
result.push({
[key]: {
current: currentState[key],
previous: previousState[key]
}
});
}
}
// check for array of objects
if (typeof value === 'object') {
const ans = compare(
value,
previousState[key][index]
);
result.push(ans);
}
});
}
}
}
return result;
};
You first need a object:
const [object, setObject] = useState({
number: 0,
text: "foo"
});
You need to check when the object changed with useEffect, but you also need to see the previos object, for that we will be using a helper function.
const prevObject = usePrevious(object);
const [result, setResult] = useState("");
useEffect(() => {
if (prevObject) {
if (object.number != prevObject.number) {
setResult("number changed");
}
if (object.text != prevObject.text) {
setResult("text changed");
}
}
}, [object]);
//Helper function to get previos
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
Here is the Codesandbox
This Google Apps Script code Search YouTube results by keywords. I want to add View Count and Subscribes Count too.
Output Data
function youTubeSearchResults() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getRange("A2:A" + sheet.getLastRow()).getValues();
const modifyResults = values.flatMap(([keywords]) => {
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
const fSearchResults = searchResults.items.filter(function (sr) { return sr.id.kind === "youtube#video" });
return fSearchResults.map(function (sr) { return [keywords, sr.id.videoId, `https://www.youtube.com/watch?v=${sr.id.videoId}`, sr.snippet.title, sr.snippet.publishedAt, sr.snippet.channelTitle, sr.snippet.channelId,`https://www.youtube.com/channel/${sr.snippet.channelId}`, sr.snippet.thumbnails.high.url] });
});
sheet.getRange(2, 2, modifyResults.length, modifyResults[0].length).setValues(modifyResults);
}
When your showing script is modified, how about the following modification?
Modified script:
function youTubeSearchResults() {
// 1. Retrieve values from column "A".
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getRange("A2:A" + sheet.getLastRow()).getDisplayValues().filter(([a]) => a);
// 2. Retrieve your current values.
const modifyResults = values.flatMap(([keywords]) => {
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
const fSearchResults = searchResults.items.filter(function (sr) { return sr.id.kind === "youtube#video" });
return fSearchResults.map(function (sr) { return [keywords, sr.id.videoId, `https://www.youtube.com/watch?v=${sr.id.videoId}`, sr.snippet.title, sr.snippet.publishedAt, sr.snippet.channelTitle, sr.snippet.channelId, `https://www.youtube.com/channel/${sr.snippet.channelId}`, sr.snippet.thumbnails.high.url] });
});
// 3. Retrieve viewCounts and subscriberCounts.
const { videoIds, channelIds } = modifyResults.reduce((o, r) => {
o.videoIds.push(r[1]);
o.channelIds.push(r[6]);
return o;
}, { videoIds: [], channelIds: [] });
const limit = 50;
const { viewCounts, subscriberCounts } = [...Array(Math.ceil(videoIds.length / limit))].reduce((obj, _) => {
const vIds = videoIds.splice(0, limit);
const cIds = channelIds.splice(0, limit);
const res1 = YouTube.Videos.list(["statistics"], { id: vIds, maxResults: limit }).items.map(({ statistics: { viewCount } }) => viewCount);
const obj2 = YouTube.Channels.list(["statistics"], { id: cIds, maxResults: limit }).items.reduce((o, { id, statistics: { subscriberCount } }) => (o[id] = subscriberCount, o), {});
const res2 = cIds.map(e => obj2[e] || null);
obj.viewCounts = [...obj.viewCounts, ...res1];
obj.subscriberCounts = [...obj.subscriberCounts, ...res2];
return obj;
}, { viewCounts: [], subscriberCounts: [] });
const ar = [viewCounts, subscriberCounts];
const rr = ar[0].map((_, c) => ar.map(r => r[c]));
// 4. Merge data.
const res = modifyResults.map((r, i) => [...r, ...rr[i]]);
// 5. Put values on Spreadsheet.
sheet.getRange(2, 2, res.length, res[0].length).setValues(res);
}
When this script is run, the following flow is run.
Retrieve values from column "A".
Retrieve your current values.
Retrieve "viewCounts" and "subscriberCounts".
Merge data.
Put values on Spreadsheet.
References:
Videos: list
Channels: list
reduce()
I have 4 dates of 4 people,I want to bring the name of the person who added that date but when I select the day the array is traversed 4 times and in one of them it brings the name.. that is to say the array travels all the people but not only the one I want.
for example on day 17 when I select it 2 alerts come out with error the third with the name of the person and the fourth alert error.. the others are similar
View picture
The code is this,the function of interest is infoDay
constructor(props) {
super(props);
this.state = {
selected: "",
usuarios: [],
};
}
componentDidMount() {
firebase
.database()
.ref("DatosCli/")
.on("child_added", (data) => {
var datos = data.val();
/* alert(JSON.stringify(datos)); */
var usuariosTemp = this.state.usuarios;
datos.key = data.key;
//Alert.alert("prueba",""+datos.longitud)
usuariosTemp.push(datos);
this.setState({ usuarios: usuariosTemp });
});
}
cargarDatos = async () => {
var userTemp = new Array();
var data = await firebase.database().ref("/DatosCli").once("value");
data.forEach((child) => {
var user = child.val();
user.key = child.key;
userTemp.push(user);
});
this.setState({ usuarios: userTemp });
};
render() {
const markedDates = {};
this.state.usuarios.forEach((usuarioTemp) => {
markedDates[usuarioTemp.date] = {
selected: true,
disableTouchEvent: false,
selectedColor: "orange",
selectedTextColor: "red",
};
});
const infoDay = (day) => {
this.state.usuarios.forEach((usuarioTemp) => {
if (day.dateString == usuarioTemp.date) {
alert(usuarioTemp.nombre);
} else {
alert("fail");
}
});
};
return (
<View style={styles.container}>
<CalendarList markedDates={markedDates} onDayPress={infoDay} />
</View>
);
}
If you remove the else statement you'll only see the correct alerts.
const infoDay = (day) => {
this.state.usuarios.forEach((usuarioTemp) => {
if (day.dateString == usuarioTemp.date) {
alert(usuarioTemp.nombre);
}
});
};
If you want to travel trough a specific user, you must have to make a dict with the user names
For example:
let usuarioTemp = {
firstUser:{day: 17},
secondUser:{day: 19}
}
And your function could it be:
const infoDay = (day, userName) => {
if(usuarioTemp[userName].date === day){
alert(userName);
}
};
And finally, I suppose you are getting the data like this:
[{nombre:"Name", date:17}, {nombre:"Name2", date:19}]
You can use this function:
function groupBy(array, key) {
let arrayReduced = array.reduce(
(result, { [key]: k, ...rest }) => {
(result[k] = rest);
return result;
},
{}
);
return arrayReduced;
}
and you'll see the data like this:
{
"Name": {date: 17},
"Name2": {date: 19}
}
Test:
//Data example
let data = [{nombre:"Name", date:17}, {nombre:"Name2", date:19}]
//Function groupBy
function groupBy(array, key) {
let arrayReduced = array.reduce(
(result, { [key]: k, ...rest }) => {
(result[k] = rest);
return result;
},
{}
);
return arrayReduced;
}
//Usage
console.log(groupBy(data, "nombre"))
I hope it helps!
This code gives the expected result, but is there a more concise way to achieve the same result? This is simply a matter of curiosity though.
The goal is to have a map representing the total students in each school, as well as a map representing the total teachers in each school.
// Example data
const studentsMap = {
student123: {
teacher: 'teacher123'
},
student456: {
teacher: 'teacher123'
},
student789: {
teacher: 'badID'
},
student000: {}
};
const teachersMap = {
teacher123: {
school: 'school123'
},
teacher456: {
school: 'school123'
},
teacher789: {
school: 'school456'
}
};
const studentsTotalBySchool = Object.keys(studentsMap).reduce((totals, key) => {
const current = studentsMap[key];
if (!teachersMap[current.teacher] || !teachersMap[current.teacher].school) {
return totals;
}
totals[teachersMap[current.teacher].school] = (totals[teachersMap[current.teacher].school] || 0) + 1;
return totals;
}, {});
const teachersTotalBySchool = Object.keys(teachersMap).reduce((totals, key) => {
const current = teachersMap[key];
totals[current.school] = (totals[current.school] || 0) + 1;
return totals;
}, {});
Is there a way to write this more succinctly without sacrificing too much readability?
You can use Object.entries and destructuring like so:
const studentsTotalBySchool = Object.entries(studentsMap).reduce((totals, [key, { teacher }) => {
if (!teachersMap[teacher] || !teachersMap[teacher].school) return totals;
totals[teachersMap[teacher].school] = (totals[teachersMap[teacher].school] || 0) + 1;
return totals;
}, {});
const teachersTotalBySchool = Object.entries(teachersMap).reduce((totals, [key, { school }) => {
totals[school] = (totals[school] || 0) + 1;
return totals;
}, {});
this will get you the same results with much less code
let schools = {
school123: {
teacher123 : {
students: ["student123", "student456"]
},
teacher456 : {
students: ["student789"]
}
},
school456: {
teacher123 : {
students: ["student123", "student456"]
},
teacher456 : {
students: ["student789"]
}
}
};
function findTotal(school, totalOf){
let accumulated = 0;
switch(totalOf){
case "students":
for(let teachers of Object.keys(schools[school])){
accumulated += schools[school][teachers].students.length;
}
break;
case "teachers":
accumulated = Object.keys(schools[school]).length;
}
return accumulated;
}
console.log(findTotal("school123", "students"))
console.log(findTotal("school123", "teachers"))
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;
}