Search code examples
javascriptloopsrecursioniterationjavascript-objects

How can I loop through a nested object and transform it?


I have an object that can have multiple levels of nested objects/arrays, values. I need to be able to loop through it, and transform it. For example

{
  arrayExample: [{
  _type: 0,
  value: 5
}, [{
  _type: 0,
  value: 1.1
}, {
  _type: 0,
  value: 1.2
}], {
  _type: 1,
  value: "hello"
}],
 simpleVal: {
   _type: 0,
  value: 10
 }
}

should become

{
  arrayExample: [5, [1.1, 1.2], new customClass("hello")],
  simpleVal: 10
}

The _type just tells me if I need to parse the value with some custom class or not. If it's 0 I just use the value as it is, and if it's not I use a custom class according to the type number.

Preferably I need an iterative solution to avoid stack overflow.

I tried using this solution as a base because it's iterative, but I'm failing to construct the resulting object correctly.

EDIT

Nina's solution almost works, but it fails with objects/nested objects. Example, the following object

const obj2 = {
    a: {
    value: 5,
    _type: 0
  },
  b: { 
    nested: {
      value: "foo",
      _type: 0
    },
    arr : [
        {
        value: 1,
        _type: 0
      },
      {
        value: 2,
        _type: 0
      },
    ]
  }
}

returns

{
  a: 5,
  b: undefined
}

when it should return

{
  a: 5,
  b: {
    nested: "foo",
    arr: [1, 2]
  }
}

EDIT

My question was closed, but I was able to find the answer because of Nina's initial solution. If the question can be reopened so I can post the answer I'd appreciate it. I will leave it here for now. It just needs to be changed to

function objMap(obj, func) {
  return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, func(v)]));
}

const getValue = v => Array.isArray(v) ? v.map(getValue) : typeof v === 'object' && v._type === undefined ? objMap(v, getValue) :getTyped(v);
const result = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, getValue(v)]));

By having a mapping function for objects similar to the map array function, Nina's solution works for nested objects/arrays as well. I don't think this is a duplicate question at all...but oh well.

EDIT

Nina's final solution works and is way cleaner.


Solution

  • For a mixed data structure you need to check if you got

    • an array,
    • a final (typed) object or just
    • a normal object.

    class CustomClass {
        constructor(value) {
            this.value = value;
        }
    }
    
    const
        data0 = { arrayExample: [{ _type: 0, value: 5 }, [{ _type: 0, value: 1.1 }, { _type: 0, value: 1.2 }], { _type: 1, value: "hello" }], simpleVal: { _type: 0, value: 10 } },
        data1 = { a: { value: 5, _type: 0 }, b: {  nested: { value: "foo", _type: 0 }, arr : [{ value: 1, _type: 0 }, { value: 2, _type: 0 }] } },
        isTyped = o => '_type' in o && 'value' in o,
        getTyped = ({ _type, value }) => {
            switch (_type) {
                case 0: return value;
                case 1: return new CustomClass(value);
            }
        },
        getValue = data => Array.isArray(data)
            ? data.map(getValue)
            : isTyped(data)
                ? getTyped(data)
                : Object.fromEntries(Object
                    .entries(data)
                    .map(([k, v]) => [k, getValue(v)])
                );
    
    console.log(getValue(data0));
    console.log(getValue(data1));
    .as-console-wrapper { max-height: 100% !important; top: 0; }