javascript - 在 Typescript/JavaScript 中从平面数组构建树数组

标签 javascript angular typescript

我有一个复杂的 json 文件,我必须使用 TypeScript/Javascript 处理它以使其分层,以便以后构建问卷。 json 的每个条目都有一个 Id(唯一)、ParentId(如果是 root,则为 0)、Text、Description。

我的 typescript 界面

export interface Question {
    Id: number;
    Text: string;
    Desc: string;
    ParentId: number;
    ChildAnswers?: Answer[];
}

export interface Answer {
    Id: number;
    Text: string;
    Desc: string;
    ParentId: number;
    ChildQuestion?: Question;
}

我可以保证,当对象是一个答案时,它只会有一个 child ,我们可以假设它是一个问题。

平面数据示例:

[
{
    Id: 1,
    Text: 'What kind of apple is it?',
    Desc: '',
    ParentId: 0
},
{
    Id: 2,
    Text: 'Green Apple',
    Desc: '',
    ParentId: 1
},
{
    Id: 3,
    Text: 'Red Apple',
    Desc: '',
    ParentId: 1
},
{
    Id: 4,
    Text: 'Purple GMO Apple',
    Desc: '',
    ParentId: 1
},
{
    Id: 5,
    Text: 'What is the issue with the apple?',
    Desc: '',
    ParentId: 2
},
{
    Id: 6,
    Text: 'Spoiled.',
    Desc: '',
    ParentId: 5
},
{
    Id: 7,
    Text: 'Taste Bad.',
    Desc: '',
    ParentId: 5
},
{
    Id: 8,
    Text: 'Too Ripe.',
    Desc: '',
    ParentId: 5
},
{
    Id: 9,
    Text: 'Is not an apple.',
    Desc: '',
    ParentId: 5
},
{
    Id: 10,
    Text: 'The apple was not green.',
    Desc: '',
    ParentId: 5
},
... So on ...
]

我的目标

{
    Id: 1,
    Text: 'What kind of apple is it?',
    Desc: '',
    ParentId: 0,
    ChildAnswers: [
        {
            Id: 2,
            Text: 'Green Apple',
            Desc: '',
            ParentId: 1,
            ChildQuestion: {
                Id: 5,
                Text: 'What is the issue with the apple?',
                Desc: '',
                ParentId: 2,
                ChildAnswers: [
                    {
                        Id: 6,
                        Text: 'Spoiled.',
                        Desc: '',
                        ParentId: 5,
                        ... So on ...
                    },
                    {
                        Id: 7,
                        Text: 'Taste Bad.',
                        Desc: '',
                        ParentId: 5,
                        ... So on ...
                    },
                    {
                        Id: 8,
                        Text: 'Too Ripe.',
                        Desc: '',
                        ParentId: 5,
                        ... So on ...
                    },
                    {
                        Id: 9,
                        Text: 'Is not an apple.',
                        Desc: '',
                        ParentId: 5,
                        ... So on ...
                    },
                    {
                        Id: 10,
                        Text: 'The apple was not green.',
                        Desc: '',
                        ParentId: 5,
                        ... So on ...
                    },
                    ... So on ...
                ]
            }
        },
        {
            Id: 3,
            Text: 'Red Apple',
            Desc: '',
            ParentId: 1,
            ... So on ...
        },
        {
            Id: 4,
            Text: 'Red Apple',
            Desc: '',
            ParentId: 1,
            ... So on ...
        }
        ... So on ...
    ]
}

我目前正在使用我在 stackoverflow 上找到的这个 list_to_tree 函数,我只是不知道如何区分问题和答案。我应该只检查问题的长度是一个还是以奇数间隔标记它?:

function list_to_tree(list) {
    var map = {}, node, roots = [], i;
    for (i = 0; i < list.length; i += 1) {
        map[list[i].Id] = i; // initialize the map
        list[i].Children = []; // initialize the children
    }
    for (i = 0; i < list.length; i += 1) {
        node = list[i];
        if (node.ParentId !== 0) {
            // if you have dangling branches check that map[node.ParentId] exists
            list[map[node.ParentId]].Children.push(node);
        } else {
            roots.push(node);
        }
    }
    return roots;
}

最佳答案

这里有一个暴力解决问题的方法:

var flat = [
{
    Id: 1,
    Text: 'What kind of apple is it?',
    Desc: '',
    ParentId: 0
},
{
    Id: 2,
    Text: 'Green Apple',
    Desc: '',
    ParentId: 1
},
{
    Id: 3,
    Text: 'Red Apple',
    Desc: '',
    ParentId: 1
},
{
    Id: 4,
    Text: 'Purple GMO Apple',
    Desc: '',
    ParentId: 1
},
{
    Id: 5,
    Text: 'What is the issue with the apple?',
    Desc: '',
    ParentId: 2
},
{
    Id: 6,
    Text: 'Spoiled.',
    Desc: '',
    ParentId: 5
},
{
    Id: 7,
    Text: 'Taste Bad.',
    Desc: '',
    ParentId: 5
},
{
    Id: 8,
    Text: 'Too Ripe.',
    Desc: '',
    ParentId: 5
},
{
    Id: 9,
    Text: 'Is not an apple.',
    Desc: '',
    ParentId: 5
},
{
    Id: 10,
    Text: 'The apple was not green.',
    Desc: '',
    ParentId: 5
},
]

// first get the roots
const tree = flat.filter((question) => question.ParentId === 0);

// Next we are going to call alternating methods recursively.
function populateQuestionChildren(node) {
  const { Id } = node;
  flat.forEach((answer) => {
    if (answer.ParentId === Id) {
      if (!node.ChildAnswers) {
        node.ChildAnswers = [];
      }
      node.ChildAnswers.push(answer);
      populateAnswerChildren(answer);
    }
  });
}

function populateAnswerChildren(node) {
  const { Id } = node;
  flat.forEach((question) => {
    if (question.ParentId === Id) {
      if (!node.ChildQuestions) {
        node.ChildQuestions = [];
      }
      node.ChildQuestions.push(question);
      populateQuestionChildren(question);
    }
  });
}

// Kick off the build for each question tree. 
tree.forEach((question) => {
  populateQuestionChildren(question);
});

很可能有更优雅的解决方案 - 但鉴于这将只有几十个或几百个问题/答案 - 这应该能满足您的需求。

[编辑]

我使用了您的接口(interface)并发现我的代码存在问题。 Answer 对象上只有一个“ChildQuestion”。所以这是我对 TypeScript 的更改,以使其正常工作。希望对您有所帮助:


interface Question {
  Id: number;
  Text: string;
  Desc: string;
  ParentId: number;
  ChildAnswers ? : Answer[];
}

interface Answer {
  Id: number;
  Text: string;
  Desc: string;
  ParentId: number;
  ChildQuestion ? : Question;
}

// first get the roots
const tree = flat.filter((question) => question.ParentId === 0);

function populateQuestionChildren(node: Question) {
  const { Id } = node;
  flat.forEach((answer) => {
    if (answer.ParentId === Id) {
      if (!node.ChildAnswers) {
        node.ChildAnswers = [];
      }
      node.ChildAnswers.push(answer);
      populateAnswerChild(answer);
    }
  });
}

function populateAnswerChild(answer: Answer) {
  const { Id } = answer;
  // switch to every so we can break early once a question is found.
  flat.every((node) => {
    if (node.ParentId === Id) {
      answer.ChildQuestion = node;
      populateQuestionChildren(node);
      return false;
    }
    return true;
  });
}

tree.forEach((question) => {
  populateQuestionChildren(question);
});

关于javascript - 在 Typescript/JavaScript 中从平面数组构建树数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58979347/

相关文章:

javascript - 如何删除具有特定 id 的 dom 元素

javascript - setInterval() 如何在切换窗口时关闭和打开

c# - 如何一次性将 json 数据和 formdata(文件)从 Angular 发送到 ASP.NET WebAPI Controller 操作?

javascript - js获取函数中调用对象的类

javascript - JavaScript 中的 GSP 标签

javascript - 对 JavaScript 语法感到困惑

javascript - Amazon S3 图像预览 url

angular - 如何检查 Angular 应用程序是在生产模式还是开发模式下运行

javascript - 有没有办法让未链接的节点不以半径分布?

javascript - 每个 React 类方法的 "Missing return type on function"