0

I am outputting data using dot notation and I am trying to convert that dot notation to bracket notation to be able to access the value for that property programatically.

What is the best way to be able to access the nested property of data.person.fullName via data['person']['fullName']

This example is vanilla JS but I it will be TypeScript. I appreciate you looking.

const data = { 
    firstName: 'Jane Doe',
    lastName: 'Doe',
    person:  {
      fullName: 'Jane Doe'
     }
}


function outputValue(path) {
 console.log('path passed:', path)
 console.log('value:', data[path])
 const isNested = new RegExp('.').test(path)
 if (isNested) {
  const nestedPath = path.split('.')
  nestedPath.forEach((path) => console.log('path:',path))
  
 }
 console.log('--------------------------------')
}



outputValue('firstName')
outputValue('person.fullName')
uber_n00b
  • 163
  • 2
  • 12
  • 1
    Are you asking about the implementation or the typings? If it's the implementation then please either remove the TypeScript tag or make your example valid TypeScript. – jcalz Mar 20 '23 at 18:32
  • 3
    [Accessing nested JavaScript objects and arrays by string path](https://stackoverflow.com/q/6491463)? – VLAZ Mar 20 '23 at 18:33
  • 2
    `path.split(".").reduce((a, k)=>a[k],data)` is going to do it for you probably, if all you care about is implementation. And then this is definitely a duplicate. – jcalz Mar 20 '23 at 18:36
  • VLAZ's indicated **duplicate** may be overkill if there are no arrays, but it's valid enough that I'll vote to close. jcalz's comment is totally valid too. – Wyck Mar 20 '23 at 19:01

1 Answers1

1

You can use String.prototype.indexOf and String.prototype.slice with a recursive sub-call.

Also, I would pass the source object into the function. Accessing a global variable inside the function is bad practice.

const data = {
  firstName: 'Jane',
  lastName: 'Doe',
  person: {
    fullName: 'Jane Doe'
  }
}

function outputValue(obj, path) {
  const index = path.indexOf('.')
  if (index === -1) {
    return obj[path]
  }
  return outputValue(obj[path.slice(0, index)], path.slice(index + 1))
}

console.log(outputValue(data, 'firstName'))
console.log(outputValue(data, 'person.fullName'))

The Lodash library has a _.get function that does just this. It is also way more flexible for some edge cases.

const data = {
  firstName: 'Jane',
  lastName: 'Doe',
  person: {
    fullName: 'Jane Doe'
  }
}

console.log(_.get(data, 'firstName'))
console.log(_.get(data, 'person.fullName'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

Here is a rudimentary TypeScript example that uses iteration:

const data = {
  firstName: 'Jane',
  lastName: 'Doe',
  person: {
    fullName: 'Jane Doe'
  },
  isActive: true
}

// Source: https://dev.to/pffigueiredo/typescript-utility-keyof-nested-object-2pa3
function get<ObjectType, TargetType>(object: ObjectType, path: string): TargetType {
  const keys = path.split('.')
  let result: any = object
  for (const key of keys) {
    result = result[key]
  }
  return result as TargetType
}

let r1: string = get(data, 'firstName')
let r2: string = get(data, 'person.fullName')
let r3: boolean = get(data, 'isActive')

; [r1, r2, r3].forEach(r => console.log(r))
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132