0

Given the following two json objects, where files represents an array of directories and colourCodes represents a mapping from a colour to status, I want to process the array of files (which is actually a list of directories) to determine a directory structure. In each file the folderColorRgb maps to a status (the status can be looked up from the colourCodes constant - if the colour doesn't exist in colourCodes then the status is Not covered).

const colourCodes = {
    '#4986e7': 'Covered 100%',
    '#f83a22': 'Covered - weak',
}


const files = [
    {
        id: '1nn7_JlbwQCxyz7qecqEtbWvY4C9Q2S3M',
        name: 'cell biology',
        parents: [ '1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR' ],
    },
    {
        id: '1bcNxav7kq--E1Qabu3tcXk9DxvRdCcgR',
        name: 'ecology',
        parents: [ '1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR' ],
        folderColorRgb: '#4986e7'
    },
    {
        id: '1MmkaP5xveClf6BfrhjHyYv4Q_Vaq1vf_',
        name: 'infection and response',
        parents: [ '1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR' ],
        folderColorRgb: '#8f8f8f'
    },
    {
        id: '1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR',
        name: 'biology',
        parents: [ '1WVm7S1fw8Neg7FM80QNxcMWvVc_w_Jt2' ],
        folderColorRgb: '#16a765'
    },
    {
        id: '1WVm7S1fw8Neg7FM80QNxcMWvVc_w_Jt2',
        name: 'science',
        parents: [ '0AF24wY_V36dlUk9PVA' ],
        folderColorRgb: '#a47ae2'
    },
    {
        id: 'physics-id',
        name: 'physics',
        parents: [ '1WVm7S1fw8Neg7FM80QNxcMWvVc_w_Jt2' ],
        folderColorRgb: '#16a765'
    },

    {
        id: 'electricity-id',
        name: 'electricity',
        parents: [ 'physics-id' ],
        folderColorRgb: '#f83a22'
    },
]

The result I want to achieve is as follows:

{
  science: {
    biology: {
      'cell biology': { status: 'Not covered' },
      ecology: { status: 'Covered 100%' },
      'infection and response': { status: 'Not covered' },
    },
    physics: { electricity: { status: 'Covered - weak' } },
  },
}

So it's the folder structure of the files array represented in JSON. You can make the following assumptions

  • The parents array of each file will always contain one parentId, that is the link to the parent folder.
  • All parentId's exist as a id in the file array - except for the top level directory (e.g. science in this case, the parentId for science does not exist in files.

Anyone know of a nice way to achieve this?

2 Answers 2

1

I think it helps to first tranform your input to a format that has just the things you need:

const Node = ({ id, name, parents: [parentId], folderColorRgb }) => ({
  id,
  name,
  parentId,
  status: colourCodes[folderColorRgb] || null,
  children: []
});

Writing the function that outputs the desired format is now easier to do:

Node.toObj = node => ({
  [node.name]: node.children.length
    ? Object.assign(...node.children.map(Node.toObj))
    : { status: node.status || "Not covered" }
});

Now the challenge is to:

  • Link up the children to their parent
  • Find the root node

I opted for creating an index of { nodeId: node } and a single forEach that creates the links and stores the root node:

const foldersById = Object.fromEntries(
  files
    .map(Node)
    .map(f => [f.id, f])
);

// Note: this mutates the nodes
let root = null;
Object
  .values(foldersById)
  .forEach(f => {
    const parent = foldersById[f.parentId];
    if (parent) parent.children.push(f);
    else root = f;
  });

Then, you output the result by calling Node.toObj(root).

The full code in a runnable snippet:

const colourCodes = {
  '#4986e7': 'Covered 100%',
  '#f83a22': 'Covered - weak',
}


const files = [{
    id: '1nn7_JlbwQCxyz7qecqEtbWvY4C9Q2S3M',
    name: 'cell biology',
    parents: ['1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR'],
  },
  {
    id: '1bcNxav7kq--E1Qabu3tcXk9DxvRdCcgR',
    name: 'ecology',
    parents: ['1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR'],
    folderColorRgb: '#4986e7'
  },
  {
    id: '1MmkaP5xveClf6BfrhjHyYv4Q_Vaq1vf_',
    name: 'infection and response',
    parents: ['1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR'],
    folderColorRgb: '#8f8f8f'
  },
  {
    id: '1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR',
    name: 'biology',
    parents: ['1WVm7S1fw8Neg7FM80QNxcMWvVc_w_Jt2'],
    folderColorRgb: '#16a765'
  },
  {
    id: '1WVm7S1fw8Neg7FM80QNxcMWvVc_w_Jt2',
    name: 'science',
    parents: ['0AF24wY_V36dlUk9PVA'],
    folderColorRgb: '#a47ae2'
  },
  {
    id: 'physics-id',
    name: 'physics',
    parents: ['1WVm7S1fw8Neg7FM80QNxcMWvVc_w_Jt2'],
    folderColorRgb: '#16a765'
  },

  {
    id: 'electricity-id',
    name: 'electricity',
    parents: ['physics-id'],
    folderColorRgb: '#f83a22'
  },
]



const Node = ({
  id,
  name,
  parents: [parentId],
  folderColorRgb
}) => ({
  id,
  name,
  parentId,
  status: colourCodes[folderColorRgb] || null,
  children: []
});

Node.toObj = node => ({
  [node.name]: node.children.length ?
    Object.assign(...node.children.map(Node.toObj)) :
    { status: node.status || "Not covered" }
});



const foldersById = Object.fromEntries(
  files
  .map(Node)
  .map(f => [f.id, f])
);

// Note: this mutates the nodes
let root = null;
Object
  .values(foldersById)
  .forEach(f => {
    const parent = foldersById[f.parentId];
    if (parent) parent.children.push(f);
    else root = f;
  });

console.log(Node.toObj(root));
.as-console-wrapper {
  min-height: 100%;
}

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks @user3297291, that's a very neat solution. I'm going to accept an answer on Wednesday.. at the moment you definitely have the best solution.
0

Ive come up with the following solution, but will not accept my own answer and will leave the question open to see if anyone has a more elegant way of doing it

const colourCodes = {
    '#4986e7': 'Covered 100%',
    '#f83a22': 'Covered - weak',
}


const files = [
    {
        id: '1nn7_JlbwQCxyz7qecqEtbWvY4C9Q2S3M',
        name: 'cell biology',
        parents: [ '1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR' ],
    },
    {
        id: '1bcNxav7kq--E1Qabu3tcXk9DxvRdCcgR',
        name: 'ecology',
        parents: [ '1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR' ],
        folderColorRgb: '#4986e7'
    },
    {
        id: '1MmkaP5xveClf6BfrhjHyYv4Q_Vaq1vf_',
        name: 'infection and response',
        parents: [ '1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR' ],
        folderColorRgb: '#8f8f8f'
    },
    {
        id: '1YewXh1WkIVIxOuHTV9acYG7WDE55EnPR',
        name: 'biology',
        parents: [ '1WVm7S1fw8Neg7FM80QNxcMWvVc_w_Jt2' ],
        folderColorRgb: '#16a765'
    },
    {
        id: '1WVm7S1fw8Neg7FM80QNxcMWvVc_w_Jt2',
        name: 'science',
        parents: [ '0AF24wY_V36dlUk9PVA' ],
        folderColorRgb: '#a47ae2'
    },
    {
        id: 'physics-id',
        name: 'physics',
        parents: [ '1WVm7S1fw8Neg7FM80QNxcMWvVc_w_Jt2' ],
        folderColorRgb: '#16a765'
    },

    {
        id: 'electricity-id',
        name: 'electricity',
        parents: [ 'physics-id' ],
        folderColorRgb: '#f83a22'
    },
]

function getStatus(folder) {
    return colourCodes[folder.folderColorRgb] || 'Not covered'
}

function extractFolderStructure(parentFolder) {

    const children = files.filter(f => f.parents.includes(parentFolder.id))
    if (children.length==0) {
        const status = getStatus(parentFolder)
        return {[parentFolder.name]: {status}}
    }
    const childObjects = children.map(c => {
        const returnObj = extractFolderStructure(c)
        return returnObj
    })

    const fullObj = {[parentFolder.name] : {}}
    childObjects.forEach(co => {
        fullObj[parentFolder.name][Object.keys(co)[0]] = co[Object.keys(co)[0]]
    })

    return fullObj
}

console.log(extractFolderStructure(files.find(f => f.name === 'science')))

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.