Search code examples
javascriptarraysrecursionrecursive-datastructures

Programmatically find specific node and add property to deep nested object


Given an array like this, where the maximum depth can be 3 levels and where we don't know at what level the researched item could be:

const data = {
         id: '1',
         children: [
             {
               id: '2',
               name: 'nameTest',
               children: [
                     {
                       id: '3'
                       name: 'deepLevel'
                      }
                ]
              }
          }

how can I add a property to the third level knowing only the value 'deepLevel' ? we are allowed to use lodash and strongly encouraged to use ES6.

the final dataStructure should be

Given an array like this, where the maximum depth can be of 3 levels:

const data = {
         id: '1',
         children: [
             {
               id: '2',
               name: 'nameTest',
               children: [
                     {
                       id: '3'
                       name: 'deepLevel'
                       addedProperty: true,
                      }
                ]
              }
        }

Solution

  • An approach was to separate the tasks of finding a nested item by a custom(izable) entry (key-value pair) and assigning additional custom data to the found item.

    Thus one e.g. could implement two methods recursivelyFindItemByEntry which is based on self recursion and a simple assignToObjectWithFirstMatchingNestedEntry which assigns provided data to the result of the former function invocation ...

    function recursivelyFindItemByEntry(obj, [key, value]) {
      let item;
    
      if (!!obj && (typeof obj === 'object')) {
        if (
          obj.hasOwnProperty(key) &&
          (obj[key] === value)
        ) {
          item = obj;
    
        } else if (
          obj.hasOwnProperty('children') &&
          Array.isArray(obj.children)
        ) {
          obj.children.some(child => {
    
            item = recursivelyFindItemByEntry(child, [key, value]);
            return !!item;
          });
        }
      }
      return item;
    }
    
    function assignToObjectWithFirstMatchingNestedEntry(obj, [key, value], data) {
      Object.assign(
        recursivelyFindItemByEntry(obj, [key, value]) ?? {},
        data ?? {}
      );
      return obj;
    }
    
    const data = {
      id: '1',
      children: [{
        id: '2',
        name: 'nameTest',
        children: [{
          id: '3',
          name: 'deepLevel',
        }, {
          id: '4',
          name: 'deepLevel',
        }],
      }, {
        id: '5',
        name: 'nameTest',
        children: [{
          id: '6',
          name: 'deepLevel',
        }, {
          id: '7',
          name: 'deepLevelTarget',
          // addedProperty: true,
        }, {
          id: '8',
          name: 'deepLevel',
        }],
      }, {
        id: '9',
        name: 'nameTest'
      }, {
        id: '10',
        name: 'nameTestTarget'
      }, {
        id: '11',
        name: 'nameTest'
      }],
    };
    
    console.log(
      "recursivelyFindItemByEntry(data, ['name', 'deepLevelTarget']) ...",
      recursivelyFindItemByEntry(data, ['name', 'deepLevelTarget'])
    );
    console.log(
      "recursivelyFindItemByEntry(data, ['id', '10']) ...",
      recursivelyFindItemByEntry(data, ['id', '10'])
    );
    console.log('\n');
    
    console.log(
      "recursivelyFindItemByEntry(data, ['id', 'foo']) ...",
      recursivelyFindItemByEntry(data, ['id', 'foo'])
    );
    console.log(
      "recursivelyFindItemByEntry(data, ['id', '1']) ...",
      recursivelyFindItemByEntry(data, ['id', '1'])
    );
    console.log('\n');
    
    console.log(
      "assignToObjectWithFirstMatchingNestedEntry(data, ['name', 'deepLevelTarget']), { addedProperty: true } ...",
      assignToObjectWithFirstMatchingNestedEntry(data, ['name', 'deepLevelTarget'], { addedProperty: true })
    );
    .as-console-wrapper { min-height: 100%!important; top: 0; }