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 samename
exist inarr2
. - 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>