Reputation: 163
So there are plenty of questions that go over how to loop through an object and make a simple change to a property name, but what I'm trying to tackle is a tad trickier and would greatly appreciate some help.
Essentially, I want to make an object like this:
{
home_number: '1234',
customer: {
name: {
last_name: 'Smith',
},
},
};
turn into this
{
home_number: '1234',
individual: {
name: {
lastName: 'Smith',
},
},
};
At the moment, my function is as follows
function restructure(obj) {
let newObj = {};
const newKeys = {
fed_tax_id: 'federalTaxId',
company_structure: 'companyStructure',
home_number: 'homeNumber',
customer: 'individual',
first_name: 'firstName',
last_name: 'lastName',
}
}
for(let key in obj){
if (typeof obj[key] === 'object') {
restructure(obj[key])
}
if(Object.hasOwnProperty.call(newKeys, key)){
let newProperty = newKeys[key]
newObj[newProperty] = obj[key]
}
if(!Object.hasOwnProperty.call(newKeys, key)){
newObj[key] = obj[key]
}
return newObj;
}
One thing I'm struggling with at the moment is removing the keys that need to be changed (I created the newKeys object as a way to show the keys that need to change and what they need to be changed to). I.e. in this case when customer is changed to individual, it will also change 'last_name' to 'lastName'.
With my function the way it is at the moment, the object comes back just as:
{ homeNumber: '1234' }
Thank you :) If this has been asked already please let me know, but after searching all over the place I wasn't able to find a question close enough to this.
Upvotes: 2
Views: 995
Reputation: 50797
I would separate the key-transformation technique from the code that applies it to your object. In this snippet, we write a generic rekey
function that takes a key-transformation function and returns a function that takes an object, running that function recursively over your keys.
So, if you want to transform all snake-case keys to camel-case ones, we can pass rekey
a simple regex-based transformation function like this:
const rekey = (fn) => (obj) =>
typeof obj == 'object'
? Object .fromEntries (
Object .entries (obj) .map (([k, v]) => [fn(k), rekey (fn) (v)])
)
: obj
const snakeToCamel = (str) =>
str .replace (/_([a-z])/g, (_, c) => c .toUpperCase ())
const obj = {home_number: '1234', customer: {name: {last_name: 'Smith'}}}
console .log (
rekey (snakeToCamel) (obj)
)
If you're working in an environment without Object .fromEntries
, it's easy to replace.
You can use either of these versions:
// more elegant
const fromEntries = (pairs) =>
pairs .reduce ((a, [k, v]) => ({... a, [k]: v}), {})
// more memory-efficient
const fromEntries = (pairs) =>
pairs .reduce ((a, [k, v]) => {a[k] = v; return a}, {})
But the point of that separation is that this is now a reusable function, and if you want to change how you transform individual keys, say from a lookup, we can just pass rekey
a different function:
const rekey = (fn) => (obj) =>
typeof obj == 'object'
? Object .fromEntries (
Object .entries (obj) .map (([k, v]) => [fn(k), rekey(fn)(v)])
)
: obj
const newKeys = {
fed_tax_id: 'federalTaxId',
company_structure: 'companyStructure',
home_number: 'homeNumber',
customer: 'individual',
first_name: 'firstName',
last_name: 'lastName',
}
const obj = {home_number: '1234', customer: {name: {last_name: 'Smith'}}}
console .log (
rekey (key => newKeys[key] || key) (obj)
)
Of course with this curried function, you can store a reference to, say, rekey (snakeToCamel)
and reuse it on many different objects.
Upvotes: 0
Reputation: 3122
You could use Object.entries
and Object.fromEntries
,
Also, create newKeys object only once, rather creating it every time function gets invoked.
const newKeys = {
fed_tax_id: 'federalTaxId',
company_structure: 'companyStructure',
home_number: 'homeNumber',
customer: 'individual',
first_name: 'firstName',
last_name: 'lastName',
}
function restructure(obj) {
let entries = Object.entries(obj).map(([key, val]) => {
val = typeof val === 'object' && !Array.isArray(val) ? restructure(val) : val
return [newKeys[key] || key, val]
});
return Object.fromEntries(entries)
}
console.log(restructure({
home_number: '1234',
customer: {
name: {
last_name: 'Smith',
},
},
}))
Upvotes: 0
Reputation: 386654
You need only a check for own property and the actual object, to prevent not own properties to be assigned.
For all properties, you could take first the new key or, if no value exists, as default the actual key.
Then you need to assign either the result of the recursive call or, if not an object just the value.
function restructure(obj) {
let newObj = {};
const newKeys = { fed_tax_id: 'federalTaxId', company_structure: 'companyStructure', home_number: 'homeNumber', customer: 'individual', first_name: 'firstName', last_name: 'lastName' };
for (let key in obj) {
if (!obj.hasOwnProperty(key)) continue;
newObj[newKeys[key] || key] = obj[key] && typeof obj[key] === 'object'
? restructure(obj[key])
: obj[key];
}
return newObj;
}
console.log(restructure({ home_number: '1234', customer: { name: { last_name: 'Smith' } } }));
Upvotes: 0
Reputation: 36574
You can use delete
to remove property from object.
Note: My function will modify the original object. If you wanna copy the object make sure to make its deep copy using JSON
methods
const newKeys = {
fed_tax_id: 'federalTaxId',
company_structure: 'companyStructure',
home_number: 'homeNumber',
customer: 'individual',
first_name: 'firstName',
last_name: 'lastName',
}
const obj = {
home_number: '1234',
individual: {
name: {
lastName: 'Smith',
},
},
};
function restructure(obj){
for(let k in obj){
if(typeof obj[k] === "object" && !Array.isArray(obj[k])){
restructure(obj[k]);
}
if(newKeys[k]){
obj[newKeys[k]] = obj[k];
delete obj[k];
}
}
}
restructure(obj);
console.log(obj)
Upvotes: 0
Reputation: 370789
Since restructure
returns the new object, you need to assign the result of recursively calling restructure
, otherwise it'll go unused, which is what's happening in your current code.
But it would probably be easier to map an array of entries instead - replace the key in the entry with the associated value on the object if the object has that key, then turn the entries back into an object with Object.fromEntries
:
const newKeys = {
fed_tax_id: 'federalTaxId',
company_structure: 'companyStructure',
home_number: 'homeNumber',
customer: 'individual',
first_name: 'firstName',
last_name: 'lastName',
};
const restructure = obj => Object.fromEntries(
Object.entries(obj).map(
([key, val]) => [
newKeys[key] || key,
typeof val === 'object' && val !== null ? restructure(val) : val
]
)
);
console.log(restructure({
home_number: '1234',
customer: {
name: {
last_name: 'Smith',
},
},
}));
Keep in mind that typeof null
gives object
, so you'll want to check for null
before recursively restructuring (as done in the above code), else you might occasionally run into errors.
Upvotes: 1