1

I want to turn all the keys of my array of objects to a camelCase in Typescript. I have the following data:

[
    {
       "Name":"Custom property",
       "Details":{
          "Address":"Huston",
          "Price":"1000000",
       },
       "Contact":{
          "Global":"3432432",
          "Local":"432423423"
       },
    },
    {
       "Name":"Myproperty",
       "Details":{
          "Address":"Huston",
          "Price":"10000001",
       },
       "Contact":{
          "Global":"34324323",
          "Local":"4324234233"
       },
    },
]

I have tried the below code, but it returns a new dictionary only with the details. How can I resolve that?

const newObjOptions = options.map((obj: any) =>
    Object.fromEntries(
      Object.entries(obj).map(([k, v]) => [_.camelCase(k), v])
    )
  );
  const newObjDetailsOptions = newObjOptions.map((obj: any) =>
    Object.fromEntries(
      Object.entries(obj.details).map(([k, v]) => [_.camelCase(k), v])
    )
  );
Enchantres
  • 853
  • 2
  • 9
  • 22
  • 1
    You can't *change* property names of an object. You can delete names you don't like and add versions that you do like. – Pointy Aug 29 '22 at 20:56

2 Answers2

2

Your implementation works, if you log both newObjOptions and newObjDetailsOptions you'll see that the top-layer of keys is indeed camel cased.

The main issue is that your code is not checking if the object contains any other objects.

const newObjOptions = options.map((obj) =>
  Object.fromEntries(
    // Here you should check if `v` is an object
    // and if so you need to camel-case that too
    Object.entries(obj).map(([k, v]) => [_.camelCase(k), v])
  )
)

To modify your code to do that you could do something like this:

// Here we check if `v` is an object and if it is we apply camel casing to that too
const camelCaseEntry = ([k, v]) => [_.camelCase(k), typeof v === 'object' ? camelCaseObject(v) : v]

const camelCaseObject = obj => Object.fromEntries(
  Object.entries(obj).map(camelCaseEntry)
)

const newObjOptions = options.map(camelCaseObject)
console.log(newObjOptions)

And that works with your example, but it breaks if your object contains an array (try it). This can be solved quite nicely with a simple recursive function, here's an idea of what that might look like:

/**
 * Takes any object and returns a new object with all keys in camel case
 */
function camelCaseObject (object) {
  if (Array.isArray(object)) {
    return object.map(camelCaseObject)
  }
  if (typeof object === 'object') {
    const entries = Object.entries(object)
    return Object.fromEntries(entries.map(transfromEntry))
  }
  return object

  function transfromEntry ([k, v]) {
    return [
      _.camelCase(k),
      typeof v === 'object' ? camelCaseObject(v) : v
    ]
  }
}
kil
  • 160
  • 1
  • 8
0

Although the provided answer by kill was accepted, there are two shortcomings when I tried to utilise it internally.

  1. It's not generic.
  2. It cannot handle null values (causing a never returning function in case a nested array contains any null values)

Let's write a generic function that can handle null values as well:

const objectify = (transformFn: (key: string) => string) => (object: object): object => {
  const transform = ([k, v]: [string, any]) => [
    transformFn(k),
    typeof v === 'object' ? objectify(transformFn)(v) : v
  ]

  if (Array.isArray(object)) {
    return object.map(objectify(transformFn))
  }

  if (typeof object === 'object' && object !== null) {
    const entries = Object.entries(object)
    return Object.fromEntries(entries.map(transform))
  }

  return object
}

export default objectify

Now let's implement the camelCase using objectify:

import { camelCase } from 'lodash'
import objectify from './objectify'

const camelify = <T extends object> (object: T): object => objectify(camelCase)(object)

export default camelify

To use this function, just pass in your objects into it: import camelify from './camelify'

const object = camelify({hello_world: 'value'}); // becomes {helloWorld: 'value'}
Farsad Fakhim
  • 148
  • 1
  • 6