3
\$\begingroup\$

I am trying to pretty print the directory structure defined in a JSON. I do have a solution using recursion but I'm looking for feedback on how this solution can be improved or if there's any better approach.

Problem Statement

A deeply nested directory contains following information:

  1. Name
  2. Data (can be an array of sub directories or file content)

Desired output: Print the directory structure in below format:

root_dir
|__dir_a
|____file_00.txt
|____dir_b
|______dir_c
|________file_01.txt
|________file_02.txt
|________file_03.txt
|________file_04.txt
|______file_05.txt
|______file_06.txt
|____dir_d
|______file_07.txt
|______file_08.txt

Solution:

My idea is to traverse data recursively till a file is not found (because beyond it there is no further nesting) and keep pushing directories and files in an array which would then be pretty printed with help of template literals.

P.S. I don't want to reverse the arrays twice, how can I fix that?

const indentation = 2;
const flatDirStructure = [];

const dirStructure = [
  {
    name: 'root_dir',
    data: [
      {
        name: 'dir_a',
        data: [
          { name: 'file_00.txt', data: 'file_00 content' },
          {
            name: 'dir_b',
            data: [
              {
                name: 'dir_c',
                data: [
                  { name: 'file_01.txt', data: 'file_01 content' },
                  { name: 'file_02.txt', data: 'file_02 content' },
                  { name: 'file_03.txt', data: 'file_03 content' },
                  { name: 'file_04.txt', data: 'file_04 content' }
                ]
              },
              { name: 'file_05.txt', data: 'file_05 content' },
              { name: 'file_06.txt', data: 'file_06 content' }
            ]
          },
          {
            name: 'dir_d',
            data: [
              { name: 'file_07.txt', data: 'file_07 content' },
              { name: 'file_08.txt', data: 'file_08 content' }
            ]
          }
        ]
      }
    ]
  }
];

const flattenDirStructure = (data, depth) => {
  if (typeof data === 'string') {
    return true;
  } else {
    data.reverse().forEach(({ name, data }) => {
      const isFile = flattenDirStructure(data, depth + indentation);

      flatDirStructure.push({
        name,
        depth,
        isFile,
      });
    });

    return false;
  }
};

const prettyPrint = () => {
  const output = [];

  flatDirStructure.reverse().forEach(({ name, depth }) => {
    const indentation = new Array(depth).fill('_').join('');
    output.push(`${indentation ? '|' : ''}${indentation}${name}`);
  });

  console.log(output.join(`\n`));
}

flattenDirStructure(dirStructure, 0);
prettyPrint();

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

You could take a single loop without reversing the array and hand over the prefix for the next level indentation.

const
    print = (data, indentation, prefix = '') => data
        .map(({ name, data }) => Array.isArray(data)
            ? prefix + name + '\n' + print(data, indentation, (prefix || '|') + '_'.repeat(indentation))
            : prefix + name
        )
        .join('\n'),
    dirStructure = [{ name: 'root_dir', data: [{ name: 'dir_a', data: [{ name: 'file_00.txt', data: 'file_00 content' }, { name: 'dir_b', data: [{ name: 'dir_c', data: [{ name: 'file_01.txt', data: 'file_01 content' }, { name: 'file_02.txt', data: 'file_02 content' }, { name: 'file_03.txt', data: 'file_03 content' }, { name: 'file_04.txt', data: 'file_04 content' }] }, { name: 'file_05.txt', data: 'file_05 content' }, { name: 'file_06.txt', data: 'file_06 content' }] }, { name: 'dir_d', data: [{ name: 'file_07.txt', data: 'file_07 content' }, { name: 'file_08.txt', data: 'file_08 content' }] }] }] }],
    result = print(dirStructure, 2);

   console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

\$\endgroup\$
1
  • \$\begingroup\$ Really neat and concise solution! \$\endgroup\$ Commented Dec 2, 2020 at 9:42
0
\$\begingroup\$

Nina has a good solution but I'm not a fan of the code style. Most IDE formatters and/or linters would complain.

This is a rewrite of it.

const dirStructure = [{
  name: 'root_dir',
  data: [{
    name: 'dir_a',
    data: [{name: 'file_00.txt', data: 'file_00 content'}, {
      name: 'dir_b',
      data: [{
        name: 'dir_c',
        data: [{name: 'file_01.txt', data: 'file_01 content'}, {name: 'file_02.txt', data: 'file_02 content'}, {name: 'file_03.txt', data: 'file_03 content'}, {name: 'file_04.txt', data: 'file_04 content'}]
      }, {name: 'file_05.txt', data: 'file_05 content'}, {name: 'file_06.txt', data: 'file_06 content'}]
    }, {name: 'dir_d', data: [{name: 'file_07.txt', data: 'file_07 content'}, {name: 'file_08.txt', data: 'file_08 content'}]}]
  }]
}]

const format = (data, i, pre = '') => data.map(({name, data}) => {
  // data is an array then it's a sub directory and needs a recursive call
  if (Array.isArray(data)) return `${pre}${name}\n${format(data, i, `${pre || '|'}${'_'.repeat(i)}`)}`
  return `${pre}${name}`
}).join('\n')

console.log(format(dirStructure, 2))

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.