Loop through Object Keys and append string to the key - javascript

I have a config object. Using this config object, I populate required elements by appending a string to the key of this object.I need help updating values
const MEMBER_INITIAL_VALUE = {
type: '',
dateOfBirth_: '',
seekingCoverage_: true,
relationshipToPrimary: ''
};
const updateInitialValue = (type, relationshipToPrimary) => {
var newMemberObjValue = JSON.parse(JSON.stringify(MEMBER_INITIAL_VALUE));
let updateValue = Object.entries(newMemberObjValue).forEach(([key, value]) => {
[`${key}_${type}`]: value; //I'm stuck here. not sure how to proceed
delete key;
});
return updateValue;
};
updateInitialValue = ('applicant', 'SELF');
updateInitialValue = ('spouse', 'DEPENDANT');
Expected Result:
{
type: 'applicant',
dateOfBirth_applicant: '',
seekingCoverage_applicant: true
relationshipToPrimary: 'SELF'
};
{
type: 'spouse',
dateOfBirth_spouse: '',
seekingCoverage_spouse: true
relationshipToPrimary: 'DEPENDANT'
};

Since you're not updating the original object, you can simplify this greatly:
const MEMBER_INITIAL_VALUE = {
type: '',
dateOfBirth_: '',
seekingCoverage_: true,
relationshipToPrimary: ''
};
const updateInitialValue = (type, relationshipToPrimary) => ({
type,
relationshipToPrimary,
[`dateOfBirth_${type}`]: MEMBER_INITIAL_VALUE.dateOfBirth_,
[`seekingCoverage_${type}`]: MEMBER_INITIAL_VALUE.seekingCoverage_
});
let updatedValue = updateInitialValue('applicant', 'SELF');
updatedValue = updateInitialValue('spouse', 'DEPENDANT');

This should do the trick:
const MEMBER_INITIAL_VALUE = {
type: '',
dateOfBirth_: '',
seekingCoverage_: true,
relationshipToPrimary: ''
};
const updateInitialValue = (type, relationshipToPrimary) => {
let newMemberInitialValue = JSON.parse(JSON.stringify(MEMBER_INITIAL_VALUE));
Object.keys(newMemberInitialValue).forEach((key) => {
if(!['type', 'relationshipToPrimary'].includes(key)) {
newMemberInitialValue[`${key}_${type}`] = newMemberInitialValue[key];
delete newMemberInitialValue[key];
}
});
newMemberInitialValue.type = type;
newMemberInitialValue.relationshipToPrimary = relationshipToPrimary;
console.log(newMemberInitialValue);
};
let applicantValues = updateInitialValue('applicant', 'SELF');
let spouseValues = updateInitialValue('spouse', 'DEPENDANT');
EDIT: Missed returning the value from the function and then assigning to a new variable.

Although an answer was posted, because i also solved it and my solution is a bit different (though the other answer looks way too slimmer) i would post it here.
const MEMBER_INITIAL_VALUE = {
type: "",
dateOfBirth_: "",
seekingCoverage_: true,
relationshipToPrimary: "",
};
const updateInitialValue = (type, relationshipToPrimary) => {
var newMemberObjValue = JSON.parse(JSON.stringify(MEMBER_INITIAL_VALUE));
Object.entries(newMemberObjValue).forEach(([key, value]) => {
if (key === "type") {
newMemberObjValue[key] = type;
} else if (key === "dateOfBirth_") {
Object.defineProperty(
newMemberObjValue,
[`${key}_${type}`],
Object.getOwnPropertyDescriptor(newMemberObjValue, key)
);
delete newMemberObjValue[key];
newMemberObjValue[`${key}_${type}`] = value;
} else if (key === "seekingCoverage_") {
Object.defineProperty(
newMemberObjValue,
[`${key}_${type}`],
Object.getOwnPropertyDescriptor(newMemberObjValue, key)
);
delete newMemberObjValue[key];
newMemberObjValue[`${key}_${type}`] = value;
} else if (key === "relationshipToPrimary") {
newMemberObjValue[key] = relationshipToPrimary;
}
});
return newMemberObjValue;
};
const updatedValue1 = updateInitialValue("applicant", "SELF");
const updatedValue2 = updateInitialValue('spouse', 'DEPENDANT');

Though a few answers have already been posted, I would like to suggest a similar one that does the same thing in a much more clear and concise way:
function Member() {
this.type = '';
this.dateOfBirth = '';
this.seekingCoverage = true;
this.relationshipToPrimary = '';
}
function UpdateInitialValue(type, relationshipToPrimary) {
var newMember = new Member();
newMember.type = type;
newMember.relationshipToPrimary = relationshipToPrimary;
return newMember;
}
console.log(UpdateInitialValue('applicant', 'SELF'));
console.log(UpdateInitialValue('spouse', 'DEPENDANT'));

Related

TypeError: Cannot read properties of undefined (reading 'vin')

For some reason I have variables outside of my function and I'm updating that variable in my function but when I call that variable in another function I get a undefined typeError
let bikeShare = []
let stations = []
function startRide(vin) {
bikeShare = bikeShare.map((bike) => {
bike.vin === vin ? { ...bike, checkOut: true } : bike
})
return {}
}
function endRide(vin) {
console.log(bikeShare)
bikeShare = bikeShare.map((bike) => {
bike.vin === vin && bike.checkOut
? { ...bike, checkOut: false, totalRides: bike.totalRides + 1 }
: bike
})
return {}
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue')
const bike_7 = createBike('green')
startRide(bike_1.vin) // in the startRide function I get an array [undefined, undefined, undefined]
endRide(bike_1.vin)
You are in the startRide() function not returning the result of each assignment in the .map method, so it returns undefined which why you see the array of undefined values.
This should fix it:
let bikeShare = []
let stations = []
function startRide(vin) {
bikeShare = bikeShare.map((bike) => {
return bike.vin === vin ? { ...bike, checkOut: true } : bike
})
return {}
}
function endRide(vin) {
console.log(bikeShare)
bikeShare = bikeShare.map((bike) => {
bike.vin === vin && bike.checkOut
? { ...bike, checkOut: false, totalRides: bike.totalRides + 1 }
: bike
})
return {}
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue');
const bike_7 = createBike('green');
startRide(bike_1.vin) // in the startRide function I get an array [undefined, undefined, undefined]
endRide(bike_1.vin)
To lift this out of comment, the body of the map argument function in startRide is enclosed in curly braces. You could remove the braces or put return bike inside the braces to stop it returning undefined.
However, setting bike.vin to a bike "payload" object with checkout set to true, leaving bike.checkout set to false, is a bug. One solution might be to use find instead of map:
let bikeShare = []
let stations = []
function startRide(vin, start = true) {
const bike = bikeShare.find(bike=>bike.vin === vin);
if( bike) {
bike.checkOut = start;
}
return bike; // for debugging
}
function endRide(vin) {
return startRide( vin, false);
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue')
const bike_7 = createBike('green')
console.log( startRide(bike_1.vin));
console.log( endRide(bike_1.vin));

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

For loop triggering more than needed

I am trying to add a Quick Launch functionality to a this homepage project which would, with a key press, launch all the URLs with quickLaunch property set to true.
I have set the scene by adding a quickLaunch property to the CONFIG object like so:
const CONFIG = {
commands: [{
{
category: 'General',
name: 'Mail',
key: 'm',
url: 'https://gmail.com',
search: '/#search/text={}',
color: 'linear-gradient(135deg, #dd5145, #dd5145)',
icon: 'mail.png',
quickLaunch: true,
},
{
category: 'General',
name: 'Drive',
key: 'd',
url: 'https://drive.google.com',
search: '/drive/search?q={}',
color: 'linear-gradient(135deg, #FFD04B, #1EA362, #4688F3)',
icon: 'drive.png',
quickLaunch: false,
},
{
category: 'Tech',
name: 'GitHub',
key: 'g',
url: 'https://github.com',
search: '/search?q={}',
color: 'linear-gradient(135deg, #2b2b2b, #3b3b3b)',
icon: 'github.png',
quickLaunch: true,
},
...and so on
and then added a condition to launch all the websites with quickLaunch option enabled:
class QueryParser {
constructor(options) {
this._commands = options.commands;
this._searchDelimiter = options.searchDelimiter;
this._pathDelimiter = options.pathDelimiter;
this._protocolRegex = /^[a-zA-Z]+:\/\//i;
this._urlRegex = /^((https?:\/\/)?[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?)$/i;
this.parse = this.parse.bind(this);
}
parse(query) {
const res = {
query: query,
split: null
};
if (this._urlRegex.test(query)) {
const hasProtocol = this._protocolRegex.test(query);
res.redirect = hasProtocol ? query : 'http://' + query;
} else {
const trimmed = query.trim();
const splitSearch = trimmed.split(this._searchDelimiter);
const splitPath = trimmed.split(this._pathDelimiter);
this._commands.some(({
category,
key,
name,
search,
url,
quickLaunch
}) => {
if (query === key) {
res.key = key;
res.isKey = true;
res.redirect = url;
return true;
}
if (splitSearch[0] === key && search) {
res.key = key;
res.isSearch = true;
res.split = this._searchDelimiter;
res.query = QueryParser._shiftAndTrim(splitSearch, res.split);
res.redirect = QueryParser._prepSearch(url, search, res.query);
return true;
}
if (splitPath[0] === key) {
res.key = key;
res.isPath = true;
res.split = this._pathDelimiter;
res.path = QueryParser._shiftAndTrim(splitPath, res.split);
res.redirect = QueryParser._prepPath(url, res.path);
return true;
}
if (key === '*') {
res.redirect = QueryParser._prepSearch(url, search, query);
}
/* ---> */ if (query === 'q!') {
for (let i = 0; i < this._commands.length; i++) {
if (this._commands[i].quickLaunch === true) {
window.open(this._commands[i].url);
}
}
return true;
}
});
}
res.color = QueryParser._getColorFromUrl(this._commands, res.redirect);
return res;
}
static _getColorFromUrl(commands, url) {
const domain = new URL(url).hostname;
return (
commands
.filter(c => new URL(c.url).hostname.includes(domain))
.map(c => c.color)[0] || null
);
}
static _prepPath(url, path) {
return QueryParser._stripUrlPath(url) + '/' + path;
}
static _prepSearch(url, searchPath, query) {
if (!searchPath) return url;
const baseUrl = QueryParser._stripUrlPath(url);
const urlQuery = encodeURIComponent(query);
searchPath = searchPath.replace('{}', urlQuery);
return baseUrl + searchPath;
}
static _shiftAndTrim(arr, delimiter) {
arr.shift();
return arr.join(delimiter).trim();
}
static _stripUrlPath(url) {
const parser = document.createElement('a');
parser.href = url;
return `${parser.protocol}//${parser.hostname}`;
}
}
I expected the marked condition (commented with "-->") to fire only once but it is doing the whole process four times all over. I logged the URLs it is trying to launch and it looks like this:
Am I missing an obvious core concept?
Seems like this._commands.some( runs through all your this._commands, and then you check if query is query === 'q!' and i guess thats always true, and if thats the case then you loop through this._commands again. giving you this._commands.length * this._commands.length amount of output.

Ag-grid angular format data before exporting

I have grid which I want to export:
initializeColumnDefs() {
this.columnDefs = [];
this.columnDefs.push({
headerName: 'time,
field: 'completedTimestamp',
cellRenderer: (params: any) => {
if (params.data.isMomentarily)
return '';
return DatagridComponent.DefaultDatetimeCellRenderer(params);
},
comparator: (valueA: number, valueB: number) => {
return DatagridComponent.DefaultDatetimeCellComparator(valueA, valueB);
}
},
{
headerName: 'people',
field: 'people',
cellRenderer: (params: any) => {
if (!params || !params.value || params.value.length <= 0)
return '';
let titles = '';
params.value.forEach(element => {
if (element.name) {
titles += element.name + ',';
}
});
return titles.substring(0, titles.length - 1);
}
}
);
}
Above there's example of two columns: one with timestamp, one with object.
My export() method which I use to export as csv:
export() {
let header = this.columnDefs.map(columnDef => {
let id = columnDef.field || columnDef.colId || columnDef.value;
let headerName = columnDef.headerName;
return headerName;
});
let a: any;
let params: any = {
fileName: 'export.csv',
columnSeparator: ';',
skipHeader: true,
columnKeys: this.columnDefs.map(c => c.field || c.colId).filter(c => !!c)
};
params.customHeader = header.join(params.columnSeparator) + '\n';
this.grid.api.exportDataAsCsv(params);
}
However, I have trouble finding how format values before exporting, because here I only get header and field and no value?
And when I export my grid to csv instead of datetime I get e.g.
which is timestamp and for my object I get
Instead of having Tom, Bob, Ben
Does anyone know how to format these values before exporting?
In your export() function, you will have to add a parameter processCellCallback.
Something like this:
export() {
let header = this.columnDefs.map(columnDef => {
let id = columnDef.field || columnDef.colId || columnDef.value;
let headerName = columnDef.headerName;
return headerName;
});
let a: any;
let params: any = {
fileName: 'export.csv',
columnSeparator: ';',
skipHeader: true,
columnKeys: this.columnDefs.map(c => c.field || c.colId).filter(c => !!c)
};
params.customHeader = header.join(params.columnSeparator) + '\n';
params.processCellCallback = function(cellParams) {
if(cellParams && cellParams.column.colId === 'yourTimestampfield') {
return this.formatter; //apply your timestamp formatter
} else if(cellParams && cellParams.column.colId === 'yourObjectfield') {
return this.formatter; //apply your object formatter
} else
return cellParams.value // no formatting
}
this.grid.api.exportDataAsCsv(params);
}
Read more in the example and docs here.

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