I need to create an array of paths based on the selected property of the hierarchical object of Arrays. The path is in form of indexes separated by a backslash for e.g. 0/0/0.
Scenario 1: selected is none
No need to store a path
Scenario 2: selected is partial - This is the one I am having issues with
No need to store a path of the current node, but the paths of child nodes with selected value all need to be stored.
Scenario 3: selected is all
Path to the current node and all its child nodes need to be stored
Expected Result: ['0', '0/0', '0/0/0', '0/0/1', '0/1', '0/1/0', '0/1/1', '1/0/0']
Current result with my logic: ["0", "0/0", "0/0/1", "1", "1/0"]
treeData = [{
name: 'Infiniti',
selected: 'all',
children: [{
name: 'G50',
selected: 'all',
children: [{
name: 'Pure AWD',
selected: 'all',
},
{
name: 'Luxe',
selected: 'all',
},
],
},
{
name: 'QX50',
selected: 'all',
children: [{
name: 'Pure AWD',
selected: 'all',
},
{
name: 'Luxe',
selected: 'all',
},
],
},
],
},
{
name: 'BMW',
selected: 'partial',
children: [{
name: '2 Series',
selected: 'partial',
children: [{
name: 'Coupé',
selected: 'all',
},
{
name: 'Gran Coupé',
selected: 'none',
},
],
},
{
name: '3 Series',
selected: 'none',
children: [{
name: 'Sedan',
selected: 'none',
},
{
name: 'PHEV',
selected: 'none',
},
],
},
],
},
];
indexPathArray = [];
path = '';
function arrayOfIndexPaths() {
for (let i = 0; i < treeData.length; i ) {
if (treeData[i].selected === 'all' || treeData[i].selected === 'partial') {
indexPathArray.push(i.toString());
if (treeData[i].children) {
updateIndexPaths(i, treeData[i].children);
}
}
}
return indexPathArray;
}
function updateIndexPaths(parentIndex, nodes) {
path = parentIndex;
for (let j = 0; j < nodes.length; j ) {
if (nodes[j].selected === 'all') {
path = path '/' j;
indexPathArray.push(path);
} else if (nodes[j].selected === 'partial' && nodes[j].children != null) {
this.updateIndexPaths(path, nodes[j].children);
}
}
}
console.log(arrayOfIndexPaths());
CodePudding user response:
Presented below is one possible way to achieve the desired objective.
Code Snippet
// helper method to iterate each array elt
// detailed explanation of code is below
const myIterator = (children, idx, noCheck, paths, parentPath) => {
const currPath = parentPath?.length ? `${parentPath}/${idx}` : idx?.toString();
if (noCheck) paths.push(currPath);
if (children?.length) return getAllSelectedPaths(
children, noCheck, paths, currPath
);
else return paths;
};
// main method to get paths when selected is 'all' or 'partial'
// detailed explanation of code is below
const getAllSelectedPaths = (
arr = [], noCheck = false, paths = [], parentPath = ''
) => (
(noCheck) ? (
arr?.reduce(
(acc, { children }, idx) => {
acc = myIterator(children, idx, noCheck, acc, parentPath);
return acc;
},
paths
)
) : (
arr?.reduce(
(acc, { children = [], selected }, idx) => {
if (['all', 'partial']?.includes(selected)) {
acc = myIterator(children, idx, selected === 'all', acc, parentPath);
};
return acc;
},
paths
)
)
);
/*
// helper method to iterate each array elt
// arguments description
// children - array of children in the parent elt
// idx - array index of parent
// noCheck - flag to determine whether (or not) to look at 'selected'
// this is useful when parent is 'all' so we process accordingly
// paths - current list of paths
// parentPath - the path to the parent elt
const myIterator = (children, idx, noCheck, paths, parentPath) => {
// first determine the "current path" as currPath
// suffix parent's path with "idx"
const currPath = parentPath?.length ? `${parentPath}/${idx}` : idx?.toString();
// if flag "noCheck" is true, add currPath to paths array
if (noCheck) paths.push(currPath);
// if there are elts in "children" array, process those
if (children?.length) return getAllSelectedPaths(
children, noCheck, paths, currPath
);
// else (ie, children is empty), simply return current "paths" array
else return paths;
};
// main method to get paths when selected is 'all' or 'partial'
// arguments description
// arr - current array being processed (initially it will be the outermost array)
// noCheck - flag to determine whether selected needs to be considered on children
// paths - array of paths
// parentPath - path of the parent elt
const getAllSelectedPaths = (
arr = [], noCheck = false, paths = [], parentPath = ''
) => (
// if the flag "noCheck" is true (ie, we don't need to look for "selected")
(noCheck) ? (
// simply reduce current array with "noCheck" as true
// it will update the paths (include elts "0", "0/0", "0/1", etc)
arr?.reduce(
(acc, { children }, idx) => {
acc = myIterator(children, idx, noCheck, acc, parentPath);
return acc;
},
paths
)
) : (
// flag "noCheck" is false
// so, we discard any elt where "selected" is not "all" or "partial"
// same "reduce" as earlier, but in this case the noCheck is determined based on
// "selected" being "all" (true) or "partial" (false)
arr?.reduce(
(acc, { children = [], selected }, idx) => {
if (['all', 'partial']?.includes(selected)) {
acc = myIterator(children, idx, selected === 'all', acc, parentPath);
};
return acc;
},
paths
)
)
);
*/
const treeData = [{
name: 'Infiniti',
selected: 'all',
children: [{
name: 'G50',
selected: 'all',
children: [{
name: 'Pure AWD',
selected: 'all',
},
{
name: 'Luxe',
selected: 'all',
},
],
},
{
name: 'QX50',
selected: 'all',
children: [{
name: 'Pure AWD',
selected: 'all',
},
{
name: 'Luxe',
selected: 'all',
},
],
},
],
},
{
name: 'BMW',
selected: 'partial',
children: [{
name: '2 Series',
selected: 'partial',
children: [{
name: 'Coupé',
selected: 'all',
},
{
name: 'Gran Coupé',
selected: 'none',
},
],
},
{
name: '3 Series',
selected: 'none',
children: [{
name: 'Sedan',
selected: 'none',
},
{
name: 'PHEV',
selected: 'none',
},
],
},
],
},
];
console.log(
'paths to selected=all are:\n',
getAllSelectedPaths(treeData)
);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.
CodePudding user response:
Imo. the rules aren't that complicated:
selected="all"
: add me and check my children.
selected="partial"
: don't add me but check my children.
selected="none"
: don't add me, don't check my children.
const treeData = [{
name: 'Infiniti',
selected: 'all',
children: [{
name: 'G50',
selected: 'all',
children: [{
name: 'Pure AWD',
selected: 'all',
},
{
name: 'Luxe',
selected: 'all',
},
],
},
{
name: 'QX50',
selected: 'all',
children: [{
name: 'Pure AWD',
selected: 'all',
},
{
name: 'Luxe',
selected: 'all',
},
],
},
],
},
{
name: 'BMW',
selected: 'partial',
children: [{
name: '2 Series',
selected: 'partial',
children: [{
name: 'Coupé',
selected: 'all',
},
{
name: 'Gran Coupé',
selected: 'none',
},
],
},
{
name: '3 Series',
selected: 'none',
children: [{
name: 'Sedan',
selected: 'none',
},
{
name: 'PHEV',
selected: 'none',
},
],
},
],
},
];
const collect = (array, prefix, acc) => array ?
array.reduce((acc, {selected, children}, i) => {
if (selected === "all") {
acc.push(prefix i);
} else if (selected !== "partial") {
return acc;
}
return collect(children, prefix i "/", acc);
}, acc) :
acc;
console.log(collect(treeData, "", []));