簡體   English   中英

從 Typescript / JavaScript 中的平面數組構建樹數組

[英]Build tree array from flat array in Typescript / JavaScript

我有一個復雜的 json 文件,我必須用 TypeScript / Javascript 處理它以使其分層,以便以后構建問卷。 json 的每個條目都有一個 Id(唯一)、ParentId(0 如果是 root)、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;
}

我可以保證當 object 是一個答案時,它只會有一個孩子,我們可以假設它是一個問題。

平面數據示例:

[
{
    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,我只是不知道如何區分問題和答案。 我應該只檢查長度是一個問題還是以奇數間隔標記它?:

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

可能有更優雅的解決方案 - 但鑒於這將只有幾十或幾百個問題/答案 - 這應該可以滿足您的需求。

[編輯]

我使用了你的接口,發現我的代碼有問題。 答案 object 上只有一個“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);
});

我根據@nephiw 的回答創建了一個答案。 由於鍵始終是問題或答案,因此奇數將始終是答案,偶數將始終是問題。 您可以簡化為一個 function 而不是兩個。

const items = [
  {
    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
  }
];

const root = items.filter(item => item.ParentId === 0);
const populateChildren = (curentItem, nested) => {
  const { Id } = curentItem;
  const key = nested % 2 === 1 ? 'ChildAnswers' : 'ChildQuestions';
  items.forEach((item) => {
    if (item.ParentId === Id) {
      if (!curentItem[key]) {
        curentItem[key] = [];
      }
      curentItem[key].push(item);
      populateChildren(item, nested + 1);
    }
  });
}
root.forEach((item) => { 
  populateChildren(item, 1);
});
console.log(root);

您可以采取一種方法,獨立於給定數據的順序收集零件,並通過切換問答方案來構建樹和 map 孩子。

 var data = [{ 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 }], tree = function (data, root) { const next = { ChildAnswers: 'ChildQuestion', ChildQuestion: 'ChildAnswers' }, toggle = type => ({ children, ...o }) => Object.assign(o, children && { [type]: children.map(toggle(next[type])) }), t = {}; data.forEach(o => { Object.assign(t[o.Id] = t[o.Id] || {}, o); t[o.ParentId] = t[o.ParentId] || {}; t[o.ParentId].children = t[o.ParentId].children || []; t[o.ParentId].children.push(t[o.Id]); }); return t[root].children.map(toggle('ChildAnswers')); }(data, 0); console.log(tree);
 .as-console-wrapper { max-height: 100%;important: top; 0; }

我的代碼:

  makeTree(nodes: any[], parentId: any): any {
return nodes
  .filter((node) => node.parentId === parentId)
  .reduce(
    (tree, node) => [
      ...tree,
      {
        ...node,
        children: this.makeTree(nodes, node.id),
      },
    ],
    []
  ); }

從數組生成節點

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM