Home > Back-end >  How to create array of paths from hierarchical object of Arrays
How to create array of paths from hierarchical object of Arrays

Time:10-12

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, "", []));

  • Related