Double for loop without mutating prop, VUE3 - javascript

I have a 'data' props which say looks like this:
data = [
{
"label":"gender",
"options":[
{"text":"m","value":0},
{"text":"f","value":1},
{"text":"x", "value":null}
]
},
{
"label":"age",
"options":[
{"text":"<30", "value":0},
{"text":"<50","value":1},
{"text":">50","value":3}
]
}
]
In a computed property I want to have a new array which looks exactly like the data prop, with the difference that - for the sake of example let's say - I want to multiply the value in the options array by 2. In plain js I did this before, like this:
data.forEach(item => {
item.options.forEach(option => {
if (option.value !== null && option.value !== 0) {
option.value *= 2;
}
})
});
Now I'm trying to do this in a computed property, with .map(), so it doesn't mutate my data props, but I cant figure out how.
computed: {
doubledValues() {
var array = this.data.map((item) => {
//...
item.options.map((option) => {
//... option.value * 2;
});
});
return array;
}
}

you can use map() method, like so:
computed: {
doubledValues() {
return this.data.map(item => ({...item, options: item.options.map(obj => {
return (obj.value != null) ? { ...obj, value: obj.value * 2 } : { ...obj }
})})
);
}
}

Just copy objects/arrays. It will be something like that
computed: {
doubledValues() {
return this.data.map((item) => {
const resultItem = {...item};
resultItem.options = item.options.map((option) => {
const copyOption = {...option};
if (copyOption.value !== null && copyOption.value !== 0) {
copyOption.value *= 2;
}
return copyOption;
});
return resultItem;
});
}
}

Related

Compare nested objects and list out differences in JavaScript

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

Vue reactivity is always one step behind

I have the following setup:
A type StringMap which I want to work like Map<> but since I'm using vue2, Map<> reactivity is not possible and I've tried to do it myself via an Array:
import Vue from "vue";
export default class StringMap {
entries: Array<[string, number]>;
constructor(data?: Record<string, number>) {
Vue.set(this, "entries", data ? Object.keys(data).map((key) => [key, data[key]]) : []);
}
get(key: string): number {
return this.entries.find((e) => e[0] === key)?.[1];
}
set(key: string, value: number): this {
const entry = this.entries.find((e) => e[0] === key);
if (entry) {
Vue.set(entry, 1, value);
}
else {
this.entries.push([key, value]);
}
return this;
}
has(key: string): boolean {
return this.entries.some((e, idx, arr) => e[0] === key);
}
delete(key: string): boolean {
const idx = this.entries.findIndex((e, idx, obj) => e[0] === key);
if (idx != -1) {
this.entries.splice(idx, 1);
}
return idx != -1;
}
clear(): void {
Vue.set(this, "entries", []);
}
}
In my Vue-Template, I have an input-field that listens to the #change method and calls calculateTotals()
<input :id="'viewModel.phasenChangeRequests[0].ppmProjektPhaseResource[' + index +'].personalInternPlanFach_PT'"
v-model="viewModel.phasenChangeRequests[0].ppmProjektPhaseResource[index].personalInternPlanFach_PT"
class="form-control"
asp-horizontal="true"
#change="calculateTotals('totalPersonalInternPlanFach_PT')"/>
And the calculateTotals is just looping over the data and summing the fields:
calculateTotals(key: string = null) {
if (this.type === "PhasenCrs") {
let totalPersonalInternPlanFachPt = 0;
for (let year = this.startyear; year < this.startyear + 5; year++) {
const phasen = this.viewModel.phasenChangeRequests;
phasen.forEach((phase) => {
var ressourcenOfYear = phase.ppmProjektPhaseResource.filter(x => x.resourceYear === year)[0];
totalPersonalInternPlanFachPt += parseFloat(ressourcenOfYear.personalInternPlanFach_PT);
});
}
if (key === null) {
console.log(`Key 'totalPersonalInternPlanFach_PT' doesn't exist yet, set it to: ${totalPersonalInternPlanFachPt}`);
this.totals2.set("totalPersonalInternPlanFach_PT", totalPersonalInternPlanFachPt);
} else {
switch (key) {
case 'totalPersonalInternPlanFach_PT':
{
console.log(`set totalPersonalInternPlanFach_PT: ${totalPersonalInternPlanFachPt}`);
this.totals2.set("totalPersonalInternPlanFach_PT", totalPersonalInternPlanFachPt);
}
}
}
}
}
totals2 is a StringMap that is initialized in the created() method:
totals2: StringMap;
async created() {
this.startyear = parseInt(moment(this.startdate, "DD.MM.YYYY").format("YYYY"));
this.projekt = JSON.parse(this.projektjson);
this.totals2 = new StringMap();
await Axios.get(this.url)
.then(res => {
this.viewModel = res.data;
})
.then(() => {
this.calculateTotals();
});
}
When I run it, it initially looks like this:
So as you can see, all fields for each year are summed correctly.
When I now change the value of 2021, the following happens:
But in the console, I can see that the total was calculated correctly:
When I now change the value back to the initial value, it shows this:
So that the previous change is now reflected in the total field but the console is showing the correct result again.
It seems that the value in the view is always one tick behind the real value...
Why is this happening?
Thanks in advance

Trying to extract common functionality among these

I have below method where i am using reducer to set these dictionaries "earliestOptionByInitialRevision" and "latestOptionByInitialRevision" inside the reducer and the code is looks like as below
const lookups = optionsInput?.reduce(
(acc, option) => {
const [optionById, earliestOptionByInitialRevision, latestOptionByInitialRevision] = acc;
optionById[option.id] = option;
const isCustomProject = option.initialRevisionId === null || option.initialRevisionId === undefined;
if (
isCustomProject ||
!earliestOptionByInitialRevision[option.initialRevisionId] ||
option.revision < earliestOptionByInitialRevision[option.initialRevisionId].revision
) {
// trying to extract the below into common
//function because in below if condition i have used the same and difference is
// "earliestOptionByInitialRevision"
if (isCustomProject) {
option = {...option, initialRevisionId:'customProjectOption'}
if (!earliestOptionByInitialRevision[option.initialRevisionId])
{
earliestOptionByInitialRevision[option.initialRevisionId] = [option];
} else {
earliestOptionByInitialRevision[option.initialRevisionId].push(option);
}
} else {
earliestOptionByInitialRevision[option.initialRevisionId] = option;
}
}
if (
isCustomProject ||
!latestOptionByInitialRevision[option.initialRevisionId] ||
option.revision > latestOptionByInitialRevision[option.initialRevisionId].revision
) {
// the below if condition same as with above and the difference
// is "latestOptionByInitialRevision"
if (isCustomProject) {
option = {...option, initialRevisionId:'customProjectOption'}
if (!latestOptionByInitialRevision[option.initialRevisionId]) {
latestOptionByInitialRevision[option.initialRevisionId] = [option];
} else {
latestOptionByInitialRevision[option.initialRevisionId].push(option);
}
} else {
latestOptionByInitialRevision[option.initialRevisionId] = option;
}
}
return acc;
},
[{}, {}, {}]
) ?? [{}, {}, {}];
const [optionById, earliestOptionByInitialRevision, latestOptionByInitialRevision] = lookups;
i would like to extract the below common functionality but could not be able to get through on how to achieve the same. Could any one please help on this that would be very grateful to me
if (isCustomProject) {
option = {...option, initialRevisionId:'customProjectOption'}
if (!earliestOptionByInitialRevision[option.initialRevisionId]) {
earliestOptionByInitialRevision[option.initialRevisionId] = [option];
} else {
earliestOptionByInitialRevision[option.initialRevisionId].push(option);
}
} else {
earliestOptionByInitialRevision[option.initialRevisionId] = option;
}
Many thanks in advance
update:
A quick fix could be to extract the common functionality into a function, parameterised by the dict (JavaScript object).
const optionsInput = [{
id: 1,
revision: 2
},
{
id: 2,
revision: 1
},
];
const lookups = optionsInput?.reduce(
(acc, option) => {
const [optionById, earliestOptionByInitialRevision, latestOptionByInitialRevision] = acc;
optionById[option.id] = option;
const isCustomProject = option.initialRevisionId === null || option.initialRevisionId === undefined;
const updateDict = (dict) => {
if (isCustomProject) {
const newOption = {...option, initialRevisionId:'customProjectOption'}
if (!dict[option.initialRevisionId]) {
dict[option.initialRevisionId] = [newOption];
} else {
dict[option.initialRevisionId].push(newOption);
}
} else {
dict[option.initialRevisionId] = option;
}
};
if (isCustomProject || !earliestOptionByInitialRevision[option.initialRevisionId] || option.revision < earliestOptionByInitialRevision[option.initialRevisionId].revision) {
updateDict(earliestOptionByInitialRevision);
}
if (isCustomProject || !latestOptionByInitialRevision[option.initialRevisionId] || option.revision > latestOptionByInitialRevision[option.initialRevisionId].revision) {
updateDict(latestOptionByInitialRevision);
}
return acc;
},
[{}, {}, {}]
) ?? [{}, {}, {}];
console.log(lookups);

Why doesn't this change my redux state?

I have an array of array of objects in my state.
What I want to do is find the question with the correct id, then find the answer with the correct id to change it's value and update it to the state.
Here is what I got:
function updateObject(oldObject, newValues) {
return Object.assign({}, oldObject, newValues);
}
function updateItemInArray(array, questionId,answerId, updateItemCallback) {
const getQuestion = array.map(item => {
if(item.id !== questionId) {
return item;
}
})
const updatedItem = getQuestion[0].answers.map(answer => {
if(answer.id !== answerId) {
return answer;
}
​
const updatedItem = updateItemCallback(answer);
return updatedItem;
});
​
return updatedItems;
}
export function answerUpdate(state = [], action){
switch(action.type){
case 'ANSWER_UPDATE_FETCH_SUCCESS': {
const newAnswer = updateItemInArray(state.project, action.questionId, action.answerId, answer => {
return updateObject(answer, {value : action.newValue});
});
}
}
}
the object I'm looking through is kinda obvious but it looks something like this
project = [
question = {
id:"some Id",
answers: [
{
id:"another id",
value="someValue"
}
]
}
]
and some other properties but it is unrelevant for this question.
Thankful for every answer!
You need to update data in map itself instead of creating variable, map function returns new array with updated value and you are updating 0th index of array which won't be one you're looking for.
function updateItemInArray(array, questionId,answerId, newValue) {
return array.map(item => {
if(item.id !== questionId) {
return item;
} else {
item.answers.map(answer => {
if(answer.id !== answerId) {
return answer;
} else {
updateObject(answer, { value : newValue})
}
});
}
});
}
export function answerUpdate(state = [], action){
switch(action.type){
case 'ANSWER_UPDATE_FETCH_SUCCESS': {
return updateItemInArray(state, action.questionId, action.answerId, action.newValue);
}
}
}

Convert returned JSON Object Properties to (lower first) camelCase

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;
}

Categories