Data Crusader
Data Crusader

Reputation: 434

How to recursively objectify a string

While trying to become more comfortable with recursive functions, I attempted to write a function that takes a dot-separated string and converts that into an object. So, given the following string: user.first.name.robert this function should return the following object:

{
  user: {
    first: {
      name: 'robert'
    }
  }
}

Here's my attempt:

function objectifyHelper(array, object) {
    if (array.length === 2) {
        const [key, value] = array;
        object[key] = value;
        return object;
    } else {
        object[array[0]] = objectifyHelper(array.slice(1), object); 
        return object;
    }
}

function objectify(string) {
    const tokens = string.split('.');
    return objectifyHelper(tokens, {});
}

const str = 'user.first.name.robert';
const result = objectify(str);

This gets me the following result:

result <ref *1> {
  name: 'robert',
  first: [Circular * 1],
  user: [Circular * 1]
}

What am I doing wrong?

Upvotes: 2

Views: 328

Answers (2)

Wiktor Zychla
Wiktor Zychla

Reputation: 48240

You are almost there. However, if your helper accepts an object but also returns it, be consistent in your recursive call

function objectifyHelper(array, object) {
    if (array.length === 2) {
        const [key, value] = array;
        object[key] = value;
        return object;
    } else {
        object[array[0]] = objectifyHelper(array.slice(1), {}); 
        return object;
    }
}

function objectify(string) {
    const tokens = string.split('.');
    return objectifyHelper(tokens, {});
}

const str = 'user.first.name.robert';
const result = objectify(str);

console.log(result)

Note that this only changes your

object[array[0]] = objectifyHelper(array.slice(1), object); 

to

object[array[0]] = objectifyHelper(array.slice(1), {}); 

Reading OP's comment under the question, I believe it's fair to further expand the answer.

Yes, passing the newly created object to the recursive function and then returning it is could be simplified. One of possible approaches would be to remove the need to pass the object into the function and just return a newly created object to the caller.

Thus, a simplified version would be

function objectifyHelper(array) {
    // we always create a new object
    var object = {};
    if (array.length === 2) {
        const [key, value] = array;
        object[key] = value;
    } else {    
        object[array[0]] = objectifyHelper(array.slice(1)); 
    }
    // and return it
    return object;
}

function objectify(string) {
    const tokens = string.split('.');
    return objectifyHelper(tokens);
}

const str = 'user.first.name.robert';
const result = objectify(str);

console.log(result);

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370789

You're only ever creating a single object here:

return objectifyHelper(tokens, {});

that then gets used every time a property is assigned to it. You need to instead create a new object when recursing, and not only inside of objectify.

A simpler approach would be:

const objectify = string => string
  .split('.')
  .reduceRight(
    (innerObj, prop) => ({ [prop]: innerObj })
  );

const str = 'user.first.name.robert';
const result = objectify(str);
console.log(result);

Upvotes: 2

Related Questions