Home > front end >  Update the parent array by checking its property: Javascript
Update the parent array by checking its property: Javascript

Time:10-19

I am having an deeply nested array of objects arr1 with certain properties, and given another array arr2 as input, the corresponding names in these needs to be compared and get the values and append it to one of the array. In case name is not present in arr2 then set to the default config, that is { val1: false, val2: false, applyToChildren: false } in case its parent applyToChildren is set to false. If its set to true, then copy its parents config to the result.

The structure looks like,

const arr1 = [
  {
    name: "internalcorp.com",
    children: [
      {
        name: "internalcorp.com.child1",
        children: [
          {
            name: "internalcorp.com.grandchild1",
            children: []
          }
        ]
      },
      {
        name: "internalcorp.com.child2",
        children: []
      }
    ]
  },
  {
    name: "internalcorpwebsite.com",
    children: []
  }
];

const arr2 = [
  {
    name: "internalcorp.com",
    val1: false,
    val2: false,
    applyToChildren: true
  },
  {
    name: "internalcorp.com.child1",
    val1: true,
    val2: true,
    applyToChildren: true
  },
  {
    name: "internalcorp.com.child2",
    val1: true,
    val2: false,
    applyToChildren: true
  },
  {
    name: "internalcorpwebsite.com",
    val1: false,
    val2: false,
    applyToChildren: true
  }
];

Output should look like,

const output = [
  {
    name: "internalcorp.com",
    config: {
      val1: false,
      val2: false,
      applyToChildren: true
    },
    children: [
      {
        name: "internalcorp.com.child1",
        config: {
          val1: true,
          val2: true,
          applyToChildren: true
        },
        children: [
          {
            name: "internalcorp.com.grandchild1",
            children: [],
            config: {
              val1: true,
              val2: true,
              applyToChildren: true
            }
          }
        ]
      },
      {
        name: "internalcorp.com.child2",
        children: [],
        config: {
          val1: true,
          val2: false,
          applyToChildren: true
        }
      }
    ]
  },
  {
    name: "internalcorpwebsite.com",
    children: [],
    config: {
      val1: false,
      val2: false,
      applyToChildren: true
    }
  }
];

Here internalcorp.com.grandchild2 is not present in arr2 and since its parents applyToChildren is true, I am copying its config to the result

Code that I tried

const addConfig = (items) =>
  items.map(item => {
    const { val1, val2, applyToChildren } = arr2.find(p => p.name === item.name);
    return {
      ...item,
      config: {
        val1,
        val2,
        applyToChildren
      },
      children: addConfig(item.children)
    };
  });

const result = addConfig(arr1);

CodePudding user response:

You could do with a simple Array.forEach

Logic.

  • Loop through each object in arr1
  • Check for each object in arr1, if a value with same name exist in arr2.
  • If exist update the config of that node.
  • If not check if its parent has applyToChildren set.
  • If set use the config from parent
  • Else use the default config
  • I have used JSON.parse(JSON.stringify(arr1)) as the paramanter, to ensure that deep copy is preformed

Worling Fiddle

const arr1 = [{name: "internalcorp.com", children: [{name: "internalcorp.com.child1",children: [ { name: "internalcorp.com.grandchild1", children: [] } ]},{name: "internalcorp.com.child2",children: []}]},{name: "internalcorpwebsite.com",children: []}];

const arr2 = [{ name: "internalcorp.com", val1: false, val2: false, applyToChildren: true },{ name: "internalcorp.com.child1", val1: true, val2: true, applyToChildren: true },{ name: "internalcorp.com.child2", val1: true, val2: false, applyToChildren: true },{ name: "internalcorpwebsite.com", val1: false, val2: false, applyToChildren: true }];

const defaultConfig = { val1: false, val2: false, applyToChildren: false };

const addConfig = (items, parentNode) => {
  items.forEach((node) => {
    const valueNode = arr2.find(value => value.name === node.name);
    if(valueNode) {
      const { name, ...config } = valueNode;
      node.config = config;
    } else {
      if (parentNode && parentNode.config && parentNode.config.applyToChildren) {
        node.config = parentNode.config;
      } else {
        node.config = defaultConfig;
      }
    }
    if(node.children && node.children.length > 0) {
      node.children.forEach(child => addConfig(node.children, node))
    }
  })
  return items;
}

const result = addConfig(JSON.parse(JSON.stringify(arr1)), null);
console.log(result);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

The following provided solution uses a recursive map based approach. It in addition utilizes the so often forgotten thisArg parameter which can be provided as map's 2nd argument where thisArg then gets provided as the callback's (the mapping function's) this context.

The solution also transforms the child-item config array into an object which gets used for easier item-specific config-lookups based on an item's name. And in order to pass down a parent specific configuration deeper into the recursion, the bound config data is provided as {lookup, config} where lookup holds the entire configuration data and config holds a reference of the most recent parent item's configuration.

function recursivelyAggregateChildItemFromBoundConfigData(item) {
  const { lookup, config: parentConfig } = this;
  const { name, children = [] } = item;
  const config = { ...(lookup[name] ?? parentConfig) };
  // { ...{} } decouples any direct `config` reference.
  return {
    name,
    config,
    children: children.map(
      recursivelyAggregateChildItemFromBoundConfigData,
      { lookup, config }
    ),
  };
}

const nestedData = [{
  name: "internalcorp.com",
  children: [{
    name: "internalcorp.com.child1",
    children: [{
      name: "internalcorp.com.grandchild1",
      children: [],
    }],
  }, {
    name: "internalcorp.com.child2",
    children: [],
  }],
}, {
  name: "internalcorpwebsite.com",
  children: []
}];

const itemConfigList = [{
  name: "internalcorp.com",
  val1: false,
  val2: false,
  applyToChildren: true
}, {
  name: "internalcorp.com.child1",
  val1: true,
  val2: true,
  applyToChildren: true
}, {
  name: "internalcorp.com.child2",
  val1: true,
  val2: false,
  applyToChildren: true
}, {
  name: "internalcorpwebsite.com",
  val1: false,
  val2: false,
  applyToChildren: true
}];

console.log(
  'lookup table/index/map ...',
  Object.fromEntries(
    itemConfigList.map(({ name:key, ...value }) => [key, value])
  )
);
console.log(
  'recursively mapped nested data ...',
  nestedData.map(recursivelyAggregateChildItemFromBoundConfigData, {
    lookup: Object.fromEntries(
      itemConfigList.map(({ name:key, ...value }) => [key, value])
    ),
    config: null,
  })
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related