Search code examples
javascriptrecursion

Can't save temp result inside recursion function


I'm trying to write a recursive function that receives an object with two branches of nested objects, and at the output returns one object with two properties that contain an array with all the specified values of the input object.

const obj = 
  { main: 
    { value: { value: 'main value'}
    , anotherValue : 'some another value'
    }
  , offhand: 
    { value: 
      { value: 'offhand value'
      , anotherValue: 'again recursion'
      }
    }
  }

function devider(object) {
  if (typeof object == 'string') {
    return object;
  }
  if (typeof object == 'object') {
    let data = {
      fromMainHand: [],
      fromOffHand: []
    };

    for (const key in object) {
      const value = object[key];
      if (typeof value == 'string') {
        data.fromMainHand.push(devider(value));
        data.fromOffHand.push(devider(value));
      }
    }
    return data;
  }
}

console.log(devider(obj)); // {fromMainHand: Array(0), fromOffHand: Array(0)}

Must be:

{ fromMainHand : ['main value', 'some another value']
, fromOffHand  : ['offhand value', 'again recursion']
}

Solution

  • Several issues:

    • Your sample input has an object literal with twice the same property. As a consequence, your input does not have the value "offhand value".> (You corrected this later in the question).

    • Your devider function returns an object with fromMainHand and fromOffHand properties, and so when you make a recursive call, with .push(devider(value)), you're adding an object to the array, not a string value or multiple string values. The object with the two properties should only be created once.

    • If the input would have numbers, booleans, or any other primitive value that is not a string, those values are ignored.

    I would split this problem into two parts:

    1. Create a function that retrieves all nested primitive values from an object.
    2. Create another function that takes an object and translates all its values with the above described function.

    The first function is a good candidate for a generator function. So then it looks like this:

    function* allValues(obj) {
        if (Object(obj) !== obj) { // It's a primitive value
            return yield obj;
        }
        for (const child of Object.values(obj)) {
            yield* allValues(child);
        }
    }
    
    function convert(obj) {
        return Object.fromEntries(Object.entries(obj).map(([key, val]) =>
            [key, [...allValues(val)]]
        ));
    }
    
    // Example run
    const obj = {
        main: {
            value: {
                value: 'main value'
            },
            anotherValue: 'some another value'
        },
        offhand: {
            value: {
                value: 'offhand value',
                anotherValue: 'again recursion'
            }
        }
    };
    
    const result = convert(obj);
    console.log(result);

    In comments you write there should be one recursive function, which can be done in many ways. For instance, you could return the flattened arrays for each property of the given object, returning that new object, and after a recursive you could flatten that object completely to an array:

    function convert(obj) {
        if (Object(obj) !== obj) { // It's a primitive value
            return [obj];
        }
        return Object.fromEntries(Object.entries(obj).map(([key, val]) =>
            [key, Object.values(convert(val)).flat()]
        ));
    }
    
    // Example run
    const obj = {
        main: {
            value: {
                value: 'main value'
            },
            anotherValue: 'some another value'
        },
        offhand: {
            value: {
                value: 'offhand value',
                anotherValue: 'again recursion'
            }
        }
    };
    
    const result = convert(obj);
    console.log(result);