Search code examples
javascriptalgorithm

How can I parse my input to have a folder structure like response


I have the folowwing input:

[
    {
        "id": "b45b8121-ea80-4ba5-970f-a963eadc0c8c",
        "name": "email",
        "idPathOfEntityToAttribute": [
            "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
            "676723c9-2795-406a-a775-d3294c095d6e",
            "c30421e8-2d32-4810-aae9-7d1a6739b41b",
            "e4f41922-8657-49f0-9831-615bd7887f33",
            "b45b8121-ea80-4ba5-970f-a963eadc0c8c"
        ]
    },
    {
        "id": "dc5b0d11-6e97-46f0-b9be-ecd4f9ab88d2",
        "name": "purpose",
        "idPathOfEntityToAttribute": [
            "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
            "676723c9-2795-406a-a775-d3294c095d6e",
            "c30421e8-2d32-4810-aae9-7d1a6739b41b",
            "e4f41922-8657-49f0-9831-615bd7887f33",
            "5ed46c31-6349-4d38-8a6f-512dbfa3d28a",
            "dc5b0d11-6e97-46f0-b9be-ecd4f9ab88d2"
        ]
    },
    {
        "id": "78e48433-0811-40d0-9b1f-c03a567eeaf9",
        "name": "usage",
        "idPathOfEntityToAttribute": [
            "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
            "676723c9-2795-406a-a775-d3294c095d6e",
            "c30421e8-2d32-4810-aae9-7d1a6739b41b",
            "e4f41922-8657-49f0-9831-615bd7887f33",
            "5ed46c31-6349-4d38-8a6f-512dbfa3d28a",
            "78e48433-0811-40d0-9b1f-c03a567eeaf9"
        ]
    },
    {
        "id": "09956860-ae67-4458-b422-323c0adb82df",
        "name": "e164Number",
        "idPathOfEntityToAttribute": [
            "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
            "676723c9-2795-406a-a775-d3294c095d6e",
            "c30421e8-2d32-4810-aae9-7d1a6739b41b",
            "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
            "09956860-ae67-4458-b422-323c0adb82df"
        ]
    },
    {
        "id": "e5249e27-58b5-4041-bc2d-e0cccf78cd80",
        "name": "number",
        "idPathOfEntityToAttribute": [
            "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
            "676723c9-2795-406a-a775-d3294c095d6e",
            "c30421e8-2d32-4810-aae9-7d1a6739b41b",
            "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
            "e5249e27-58b5-4041-bc2d-e0cccf78cd80"
        ]
    },
    {
        "id": "a881cfca-2655-4270-a317-7a83d15c3b73",
        "name": "testArray",
        "idPathOfEntityToAttribute": [
            "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
            "676723c9-2795-406a-a775-d3294c095d6e",
            "c30421e8-2d32-4810-aae9-7d1a6739b41b",
            "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
            "a881cfca-2655-4270-a317-7a83d15c3b73"
        ]
    },

    {
        "id": "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
        "name": "companyInfo"
    },
    {
        "id": "676723c9-2795-406a-a775-d3294c095d6e",
        "name": "primaryContact"
    },
    {
        "id": "c30421e8-2d32-4810-aae9-7d1a6739b41b",
        "name": "contactInfo"
    },
    {
        "id": "e4f41922-8657-49f0-9831-615bd7887f33",
        "name": "emails"
    },
    {
        "id": "5ed46c31-6349-4d38-8a6f-512dbfa3d28a",
        "name": "variation"
    },
    {
        "id": "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
        "name": "phoneNumbers"
    }
]

  • We can assume that the last item of the any idPathOfEntityToAttribute array is the 'file' attribute

  • Anything else above the last item is considered a 'folder'

  • The ones that doesn't have idPathOfEntityToAttribute are references to the folders

  • Need to allocate each item in the correct folder

  • I need to reduce this to have a folder hierchy style check the expected output

I have tried multiple approaches like recursive/plain reduce functions but no luck, i tried the following:

const assignFolders = (
  dataMapNodes: DataMapSearchNode[],
  pathIds: string[],
  pathId: string,
  parentId?: string,
): DattaMapNodeInput | null => {
  if (pathIds.length === 0) {
    return null;
  }

  const currentDataNode = dataMapNodes.find((node) => node.id === pathId);
  const lastDomainId = pathIds[pathIds.length - 1];

  if (currentDataNode) {
    const newPathId = pathIds.slice(1);

    // const shareSameLastDomain = dataMapNodes.filter((dataMap) => {
    //   const currentPathId = dataMap.subAttributes?.idPathOfEntityToAttribute;
    //   if (currentPathId && currentPathId[currentPathId.length - 1] === lastDomainId) {
    //     return dataMap;
    //   }
    //   return null;
    // });

    const children = assignFolders(
      dataMapNodes,
      newPathId,
      newPathId[0],
      `${parentId}-sub-0`,
    );

    return {
      ...currentDataNode,
      id: `${parentId}-sub-0`,
      type: currentDataNode.dataMapType,
      name: currentDataNode.name,
      parentId,
      children: (children && [children]) || null,
    };
  }

  return null;
};

this is the expected output:

[
  {
    "id": "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
    "name": "companyInfo",
    "children": [
      {
        "id": "676723c9-2795-406a-a775-d3294c095d6e",
        "name": "primaryContact",
        "children": [
          {
            "id": "c30421e8-2d32-4810-aae9-7d1a6739b41b",
            "name": "contactInfo",
            "children": [
              {
                "id": "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
                "name": "phoneNumbers",
                "children": [
                  {
                    "id": "09956860-ae67-4458-b422-323c0adb82df",
                    "name": "e164Number",
                    "idPathOfEntityToAttribute": [
                      "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
                      "676723c9-2795-406a-a775-d3294c095d6e",
                      "c30421e8-2d32-4810-aae9-7d1a6739b41b",
                      "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
                      "09956860-ae67-4458-b422-323c0adb82df"
                    ]
                  },
                  {
                    "id": "e5249e27-58b5-4041-bc2d-e0cccf78cd80",
                    "name": "number",
                    "idPathOfEntityToAttribute": [
                      "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
                      "676723c9-2795-406a-a775-d3294c095d6e",
                      "c30421e8-2d32-4810-aae9-7d1a6739b41b",
                      "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
                      "e5249e27-58b5-4041-bc2d-e0cccf78cd80"
                    ]
                  },
                  {
                    "id": "a881cfca-2655-4270-a317-7a83d15c3b73",
                    "name": "testArray",
                    "idPathOfEntityToAttribute": [
                      "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
                      "676723c9-2795-406a-a775-d3294c095d6e",
                      "c30421e8-2d32-4810-aae9-7d1a6739b41b",
                      "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
                      "a881cfca-2655-4270-a317-7a83d15c3b73"
                    ]
                  }
                ]
              }
            ]
          },
          {
            "id": "e4f41922-8657-49f0-9831-615bd7887f33",
            "name": "emails",
            "children": [
              {
                "id": "5ed46c31-6349-4d38-8a6f-512dbfa3d28a",
                "name": "variation",
                "children": [
                  {
                    "id": "dc5b0d11-6e97-46f0-b9be-ecd4f9ab88d2",
                    "name": "purpose",
                    "idPathOfEntityToAttribute": [
                      "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
                      "676723c9-2795-406a-a775-d3294c095d6e",
                      "c30421e8-2d32-4810-aae9-7d1a6739b41b",
                      "e4f41922-8657-49f0-9831-615bd7887f33",
                      "5ed46c31-6349-4d38-8a6f-512dbfa3d28a",
                      "dc5b0d11-6e97-46f0-b9be-ecd4f9ab88d2"
                    ]
                  },
                  {
                    "id": "78e48433-0811-40d0-9b1f-c03a567eeaf9",
                    "name": "usage",
                    "idPathOfEntityToAttribute": [
                      "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
                      "676723c9-2795-406a-a775-d3294c095d6e",
                      "c30421e8-2d32-4810-aae9-7d1a6739b41b",
                      "e4f41922-8657-49f0-9831-615bd7887f33",
                      "5ed46c31-6349-4d38-8a6f-512dbfa3d28a",
                      "78e48433-0811-40d0-9b1f-c03a567eeaf9"
                    ]
                  }
                ]
              },
              {
                "id": "b45b8121-ea80-4ba5-970f-a963eadc0c8c",
                "name": "email",
                "idPathOfEntityToAttribute": [
                  "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
                  "676723c9-2795-406a-a775-d3294c095d6e",
                  "c30421e8-2d32-4810-aae9-7d1a6739b41b",
                  "e4f41922-8657-49f0-9831-615bd7887f33",
                  "b45b8121-ea80-4ba5-970f-a963eadc0c8c"
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]


Solution

  • Inspired from Build tree array from flat array in javascript

    This is a few steps process comments are in code.

    1. For simplicity, we sort by longest path so that we can create correct path for our parents that has no path (they could be references). If order does matter, let me know.
    2. For simplicity, we build a dictionary object to access items by their id.
    3. Now indeed for each item we first make sure that each of its parents, has the correct parents as well (minus one)
    4. Now we can attach every item to it's parent node children, or to the root of the tree.

    var arr = [{
        "id": "b45b8121-ea80-4ba5-970f-a963eadc0c8c",
        "name": "email",
        "idPathOfEntityToAttribute": [
          "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
          "676723c9-2795-406a-a775-d3294c095d6e",
          "c30421e8-2d32-4810-aae9-7d1a6739b41b",
          "e4f41922-8657-49f0-9831-615bd7887f33",
          "b45b8121-ea80-4ba5-970f-a963eadc0c8c"
        ]
      },
      {
        "id": "dc5b0d11-6e97-46f0-b9be-ecd4f9ab88d2",
        "name": "purpose",
        "idPathOfEntityToAttribute": [
          "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
          "676723c9-2795-406a-a775-d3294c095d6e",
          "c30421e8-2d32-4810-aae9-7d1a6739b41b",
          "e4f41922-8657-49f0-9831-615bd7887f33",
          "5ed46c31-6349-4d38-8a6f-512dbfa3d28a",
          "dc5b0d11-6e97-46f0-b9be-ecd4f9ab88d2"
        ]
      },
      {
        "id": "78e48433-0811-40d0-9b1f-c03a567eeaf9",
        "name": "usage",
        "idPathOfEntityToAttribute": [
          "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
          "676723c9-2795-406a-a775-d3294c095d6e",
          "c30421e8-2d32-4810-aae9-7d1a6739b41b",
          "e4f41922-8657-49f0-9831-615bd7887f33",
          "5ed46c31-6349-4d38-8a6f-512dbfa3d28a",
          "78e48433-0811-40d0-9b1f-c03a567eeaf9"
        ]
      },
      {
        "id": "09956860-ae67-4458-b422-323c0adb82df",
        "name": "e164Number",
        "idPathOfEntityToAttribute": [
          "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
          "676723c9-2795-406a-a775-d3294c095d6e",
          "c30421e8-2d32-4810-aae9-7d1a6739b41b",
          "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
          "09956860-ae67-4458-b422-323c0adb82df"
        ]
      },
      {
        "id": "e5249e27-58b5-4041-bc2d-e0cccf78cd80",
        "name": "number",
        "idPathOfEntityToAttribute": [
          "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
          "676723c9-2795-406a-a775-d3294c095d6e",
          "c30421e8-2d32-4810-aae9-7d1a6739b41b",
          "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
          "e5249e27-58b5-4041-bc2d-e0cccf78cd80"
        ]
      },
      {
        "id": "a881cfca-2655-4270-a317-7a83d15c3b73",
        "name": "testArray",
        "idPathOfEntityToAttribute": [
          "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
          "676723c9-2795-406a-a775-d3294c095d6e",
          "c30421e8-2d32-4810-aae9-7d1a6739b41b",
          "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
          "a881cfca-2655-4270-a317-7a83d15c3b73"
        ]
      },
    
      {
        "id": "a12b7e26-6a50-422e-81a0-1c71598cdfb8",
        "name": "companyInfo"
      },
      {
        "id": "676723c9-2795-406a-a775-d3294c095d6e",
        "name": "primaryContact"
      },
      {
        "id": "c30421e8-2d32-4810-aae9-7d1a6739b41b",
        "name": "contactInfo"
      },
      {
        "id": "e4f41922-8657-49f0-9831-615bd7887f33",
        "name": "emails"
      },
      {
        "id": "5ed46c31-6349-4d38-8a6f-512dbfa3d28a",
        "name": "variation"
      },
      {
        "id": "ba03a1af-3a9a-466b-b5ec-8d744f620e66",
        "name": "phoneNumbers"
      }
    ]
    
    function unflatten(arr) {
      var tree = []
    
      // sort by depth (leaves first)
      arr.sort(function(a, b) {
        var len_a = a.idPathOfEntityToAttribute ? a.idPathOfEntityToAttribute.length : 0
        var len_b = b.idPathOfEntityToAttribute ? b.idPathOfEntityToAttribute.length : 0
        return len_b - len_a
      })
    
      // group by id for easy access
      var grouped = arr.reduce(function(agg, item) {
        agg[item.id] = item
        item.children = []
        return agg;
      }, {})
    
      Object.keys(grouped).forEach(function(key) {
        var item = grouped[key];
    
        // make sure ancestors has correct idPathOfEntityToAttribute
        var path = [...item.idPathOfEntityToAttribute]
        var folder = path.pop()
        while (path.length) {
          folder = path.pop()
          grouped[folder].idPathOfEntityToAttribute = [...path, folder]
        }
    
        // attach to the tree or to the parent
        var len = item.idPathOfEntityToAttribute.length
        var parent_id = len > 1 ? item.idPathOfEntityToAttribute[len - 2] : null
        if (parent_id) {
          var parent = grouped[parent_id]
          if (!parent) {
            throw new Error("missing parent: " + parent_id + " - possible create it?")
          }
          parent.children.push(item);
        } else {
          tree.push(item)
        }
      })
    
      // optionally clean by removing idPathOfEntityToAttribute
      Object.keys(grouped).forEach(function(key) {
        var item = grouped[key];
        delete item.idPathOfEntityToAttribute
      })
    
      return tree;
    }
    
    
    var tree = unflatten(arr)
    
    console.log(tree)
    .as-console-wrapper {
      max-height: 100% !important;
      min-height: 100% !important;
    }