Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

update nested json object using recursion in JavaScript

I'm trying to create an updated object from an existing object. The sample object is:

// sample object
const testObj = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3,
      f: {
        g: 4
      }
    }
  }
};

I want to create a new object from the above object with some concatenation of each value:

// expected object
const expectedObject= {
  a: '1 a',
  b: {
    c: '2 a',
    d: {
      e: '3 a',
      f: {
        g: '4 a'
      }
    }
  }
};

here is my sample code:

let expectedObject = {};
const newObject = object => {
  Object.entries(object).forEach(([key, value]) => {
    if (typeof value === "object") {
      Object.keys(value).map(key => {
        value[key] = value[key] + " a";
        return value;
      });

      expectedObject[key] = value;
      //return newTest;
    } else {
      expectedObject[key] = value;
      return expectedObject;
    }
  });
  return expectedObject;
};
console.log(newObject(testObj));

the outcome in console is:

{a: 1, b: {…}}
a: 1
b:
c: "2 a"
d: "[object Object] a"
__proto__: Object
__proto__: Object

I wanted to use recursion here and also tried it but no luck. any help, thanks?

like image 544
Yahya Avatar asked Sep 14 '25 17:09

Yahya


2 Answers

You could get a new object my mapping changed values and creating new objects.

function map(object, fn) {
    return Object.fromEntries(Object
        .entries(object)
        .map(([k, v]) => [k, v && typeof v === 'object' ? map(v, fn) : fn(v)])
    );
}

var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4 } } } },
    result = map(object, v => v + ' a');

console.log(result);

If you have arrays inside, you could add a check in advance and map the values.

const
    map = fn => {
        const iter = v => v && typeof v === 'object'
            ? Array.isArray(v)
                ? v.map(iter)
                : Object.fromEntries(Object.entries(v).map(([k, v]) => [k, iter(v, fn)]))
            : fn(v);
        return iter;
    };

var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4, k: [5, 6] } } } },
    addA = map(v => v + ' a'),
    result = addA(object);

console.log(result);
like image 65
Nina Scholz Avatar answered Sep 16 '25 05:09

Nina Scholz


This is simply a refactoring of the answer from @user633183. I like that approach a lot, but think it can be simplified by extracting two more reusable functions. This started as a comment on that answer, but I thought it would be better to be explicit.

const map = (f) => (a) => 
  a.map(f)

const mapObj = (f) => (o) => 
  Object .entries (o) .reduce ( (a, [k, v] ) => ({ ...a, [k]: f(v) }), {})

const traverse = (f) => (t) =>
  Array.isArray(t)
    ? map (traverse (f)) (t)
  : Object(t) === t 
    ? mapObj (traverse (f)) (t)
  : f (t)

const input =
  { a: [ 1, 11, 111 ], b: { c: 2, d: { e: [ 3, { f: { g: 4 } } ] } } }

const output =
  traverse(x => `${x} a`) (input)

console.log(output)

mapObj can be written in many different ways. Here are two alternatives:

const mapObj = (f = identity) => (o = {}) => 
  Object .fromEntries (Object .entries (o) .map (([ k, v ]) => [ k, f (v) ]))


const mapObj = (f = identity) => (o = {}) => 
  Object .assign .apply (null, Object .entries (o) .map (([ k, v ]) => ({ [k]: f (v) 
like image 30
Scott Sauyet Avatar answered Sep 16 '25 07:09

Scott Sauyet