Обработка некоторого JSON для вывода объекта JSON, представляющего структуру папок

#javascript #node.js #json #functional-programming

#javascript #node.js #json #функциональное программирование

Вопрос:

Учитывая следующие два объекта json, где files представляет собой массив каталогов и colourCodes представляет отображение из цвета в status , я хочу обработать массив файлов (который на самом деле является списком каталогов), чтобы определить структуру каталогов. В каждом file folderColorRgb из них отображается статус (статус можно посмотреть из colourCodes константы — если цвет не существует, colourCodes тогда статус есть 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'
    },
]
  

Результат, которого я хочу достичь, заключается в следующем:

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

Таким образом, это структура папок files массива, представленного в JSON. Вы можете сделать следующие предположения

  • parents Массив каждого файла всегда будет содержать один parentId , то есть ссылку на родительскую папку.
  • Все parentId's они существуют как a id в файловом массиве — за исключением каталога верхнего уровня (например, science в этом случае родительский идентификатор для science не существует files .

Кто-нибудь знает хороший способ добиться этого?

Ответ №1:

Я думаю, что это помогает сначала преобразовать ваш ввод в формат, в котором есть только то, что вам нужно:

 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" }
});
  

Теперь задача состоит в том, чтобы:

  • Свяжите дочерние элементы с их родительскими
  • Найдите корневой узел

Я выбрал создание индекса { nodeId: node } и одного forEach , который создает ссылки и сохраняет корневой узел:

 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;
  });
  

Затем вы выводите результат путем вызова Node.toObj(root) .

Полный код в выполняемом фрагменте:

 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%;
}  

Комментарии:

1. Спасибо @user3297291, это очень удобное решение. Я собираюсь принять ответ в среду .. на данный момент у вас определенно есть лучшее решение.

Ответ №2:

Я придумал следующее решение, но не приму свой собственный ответ и оставлю вопрос открытым, чтобы посмотреть, есть ли у кого-нибудь более элегантный способ сделать это

 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')))