Home > Software engineering >  Flattening an array of objects based on specific keys
Flattening an array of objects based on specific keys

Time:01-07

I am currently making a tree view, where there will be folders and subfolders. The code is as such from my json file:

    [
        {
          "name": "Knowledge Base",
          "files": [
            "knowledge-base.pdf",
            "hello-word.pdf"
          ],
          "folders": [
            {
              "name": "Documents",
              "files": [
                "file1.pdf",
                "file2.pdf",
                "file3.pdf"
              ],
              "folders": [
                {
                  "name": "Important Documents",
                  "files": [
                    "I like trains",
                    "Why",
                    "OMG NOOOO"
                  ],
                  "folders": [
                    {
                      "name": "Hello World",
                      "files": [
                        "Hell no"
                      ]
                    }
                  ]
                },
    
                {
                  "name": "My secrets",
                  "files": [
                    "Pay",
                    "I dont like my boss",
                    "Hobbies"
                  ]
                }
              ]
            },
            {
              "name": "Images",
              "files": [
                "image1.png",
                "image2.png",
                "image3.png",
                "image4.png",
                "image5.png"
              ],
              "folders": ""
            },
            {
              "name": "Important",
              "files": [
                "confidential.pdf",
                "important.pdf"
              ],
              "folders": ""
            }
          ]
        },
        {
          "name": "Downloads",
          "files": [
            "download1.pdf",
            "download2.pdf",
            "download3.pdf"
          ],
          "folders": ""
        },
        {
          "name": "Favourites",
          "files": [
            "favourite1.pdf",
            "favourite2.pdf",
            "favourite3.pdf",
            "favourite4.pdf"
          ],
          "folders": ""
        }
      ]

A new folder is denoted with a new object {}, which consists of its name, files and folders within it, if any.

I would like to flatten the dictionary such that it outputs all files (with a file is denoted by a . and a folder is denoted by a >:

For example, for the documents folder:

  • Knowledge Base>Documents.file1.pdf
  • Knowledge Base>Documents.file2.pdf
  • Knowledge Base>Documents.file3.pdf
  • Knowledge Base>Documents>Important Documents.I like trains
  • Knowledge Base>Documents>Important Documents.Why
  • Knowledge Base>Documents>Important Documents.OMG NOOOO
  • Knowledge Base>Documents>Important Documents>Hello World.Hell no
  • Knowledge Base>Documents>My Secrets.I dont like my boss
  • Knowledge Base>Documents>My Secrets.Hobbies

CodePudding user response:

Recursive function is to the resqueue.

The idea behind recursive function is that it accepts an object as parameter and if a child of that object contains folders, loop through array of these folders and send each folder object back to the same function.

const data = [
  {
    "name": "Knowledge Base",
    "files": [
      "knowledge-base.pdf",
      "hello-word.pdf"
    ],
    "folders": [
      {
        "name": "Documents",
        "files": [
          "file1.pdf",
          "file2.pdf",
          "file3.pdf"
        ],
        "folders": [
          {
            "name": "Important Documents",
            "files": [
              "I like trains",
              "Why",
              "OMG NOOOO"
            ],
            "folders": [
              {
                "name": "Hello World",
                "files": [
                  "Hell no"
                ]
              }
            ]
          },

          {
            "name": "My secrets",
            "files": [
              "Pay",
              "I dont like my boss",
              "Hobbies"
            ]
          }
        ]
      },
      {
        "name": "Images",
        "files": [
          "image1.png",
          "image2.png",
          "image3.png",
          "image4.png",
          "image5.png"
        ],
        "folders": ""
      },
      {
        "name": "Important",
        "files": [
          "confidential.pdf",
          "important.pdf"
        ],
        "folders": ""
      }
    ]
  },
  {
    "name": "Downloads",
    "files": [
      "download1.pdf",
      "download2.pdf",
      "download3.pdf"
    ],
    "folders": ""
  },
  {
    "name": "Favourites",
    "files": [
      "favourite1.pdf",
      "favourite2.pdf",
      "favourite3.pdf",
      "favourite4.pdf"
    ],
    "folders": ""
  }
];

function flatten(obj, parent = "")
{
  parent  = obj.name   ">"; //append current folder name
  let result = obj.files ? obj.files.map(file => parent   file) : [];//add files
  if (Array.isArray(obj.folders))
    result = result.concat(...obj.folders.map(folder => flatten(folder, parent))); //recursivly call flatten for next subfolder

  return result;
}


const dataFlat = data.reduce((a,b) => (a.push(...flatten(b)), a), []);

console.log(dataFlat);
.as-console-wrapper{top:0;max-height:unset!important;overflow:auto!important;}

CodePudding user response:

For that you need to do a recursive identifying when it is an array, a folder or a file.

const treeView = [
    {
        name: "Knowledge Base",
        files: [
            "knowledge-base.pdf",
            "hello-word.pdf"
        ],
        folders: [
            {
                name: "Documents",
                files: [
                    "file1.pdf",
                    "file2.pdf",
                    "file3.pdf"
                ],
                folders: [
                    {
                        name: "Important Documents",
                        files: [
                            "I like trains",
                            "Why",
                            "OMG NOOOO"
                        ],
                        folders: [
                            {
                                name: "Hello World",
                                files: [
                                    "Hell no"
                                ]
                            }
                        ]
                    },

                    {
                        name: "My secrets",
                        files: [
                            "Pay",
                            "I dont like my boss",
                            "Hobbies"
                        ]
                    }
                ]
            },
            {
                name: "Images",
                files: [
                    "image1.png",
                    "image2.png",
                    "image3.png",
                    "image4.png",
                    "image5.png"
                ],
                folders: ""
            },
            {
                name: "Important",
                files: [
                    "confidential.pdf",
                    "important.pdf"
                ],
                folders: ""
            }
        ]
    },
    {
        name: "Downloads",
        files: [
            "download1.pdf",
            "download2.pdf",
            "download3.pdf"
        ],
        folders: ""
    },
    {
        name: "Favourites",
        files: [
            "favourite1.pdf",
            "favourite2.pdf",
            "favourite3.pdf",
            "favourite4.pdf"
        ],
        folders: ""
    }
]

function handleObj(obj, type, path = '') {
    const result = []
    if (Array.isArray(obj)) { // recursive on arrays
        for (const thisObj of obj) {
            result.push(handleObj(thisObj, type, path))
        }
    } else {
        type = type || getType(obj)
        if (type === 'folder') { // recursive on folders
            const {
                name,
                files,
                folders
            } = obj
            path = path ? `${path}>${name}` : name
            if (files && files.length) {
                result.push(handleObj(files, 'file', path))
            }
            if (folders && folders.length) {
                result.push(handleObj(folders, 'folder', path))
            }
        } else if (type === 'file') {
            const filename = obj
            if (path) {
                result.push(`${path}.${filename}`)
            } else {
                result.push(`${filename}`)
            }
        }
    }
    return result.join('\n')
}

function getType(obj) {
    const {
        name,
        files,
        folders
    } = obj
    if (name !== undefined && files !== undefined && folders !== undefined) {
        return 'folder'
    }
    return 'file'
}

console.log(handleObj(treeView))

CodePudding user response:

You can use a basic recusrive function like this:

const tree = [{
    "name": "Knowledge Base",
    "files": [
      "knowledge-base.pdf",
      "hello-word.pdf"
    ],
    "folders": [{
        "name": "Documents",
        "files": [
          "file1.pdf",
          "file2.pdf",
          "file3.pdf"
        ],
        "folders": [{
            "name": "Important Documents",
            "files": [
              "I like trains",
              "Why",
              "OMG NOOOO"
            ],
            "folders": [{
              "name": "Hello World",
              "files": [
                "Hell no"
              ]
            }]
          },

          {
            "name": "My secrets",
            "files": [
              "Pay",
              "I dont like my boss",
              "Hobbies"
            ]
          }
        ]
      },
      {
        "name": "Images",
        "files": [
          "image1.png",
          "image2.png",
          "image3.png",
          "image4.png",
          "image5.png"
        ],
        "folders": ""
      },
      {
        "name": "Important",
        "files": [
          "confidential.pdf",
          "important.pdf"
        ],
        "folders": ""
      }
    ]
  },
  {
    "name": "Downloads",
    "files": [
      "download1.pdf",
      "download2.pdf",
      "download3.pdf"
    ],
    "folders": ""
  },
  {
    "name": "Favourites",
    "files": [
      "favourite1.pdf",
      "favourite2.pdf",
      "favourite3.pdf",
      "favourite4.pdf"
    ],
    "folders": ""
  }
]

var result = []

function rec(folders, acc) {
  if (folders) {
    
    folders.forEach(folder => {
      const newAcc = !!acc ? `${acc}>${folder.name}` : `${folder.name}`

      if (folder.files) {
        const newFiles = folder.files.map(file => `${newAcc}.${file}`)
        result = [...result, ...newFiles]
      }

      if (folder.folders) {
        rec(folder.folders, newAcc)
      }
    })
  }


}

rec(tree, '')

console.log(result)

  • Related