【问题标题】:convert flat array to nested array将平面数组转换为嵌套数组
【发布时间】:2021-01-02 10:34:48
【问题描述】:

我有一个像这样的 javascript 对象数组:

// Id is not necessarily unique, orderly or for any specific purpose
var input = [
    { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
    { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
    { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
    { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
    { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
    { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
    { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
    { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
    { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
    { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
    { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" },
    //...
];

如何将它转换成这样的东西?

var output = [
    {
        Name: "Europe",
        Children: [
            {
                Name: "Germany",
                Children: [
                    {
                        Name: "Frankfurt",
                        Id: 1,
                        Attribute1: "some attribute"
                    },
                    {
                        Name: "Munich",
                        Id: 2,
                        Attribute1: "some attribute"
                    }
                ]
            },
            {
                Name: "France",
                Children: [
                    {
                        Name: "Paris",
                        Id: 12,
                        Attribute1: "some attribute"
                    },
                    {
                        Name: "Marseille",
                        Id: 14,
                        Attribute1: "some attribute"
                    }
                ]
            }
        ],
        //...
    },
    //...
];

我做了一些搜索,发现了一些非常相似的主题: Transform array to object tree [JS] array of strings to tree data structure 但我想要的是嵌套数组和对象的组合,而不是上述解决方案中的对象树。

请帮帮我,谢谢!

【问题讨论】:

  • 使用LongNamesplit(';') 获取名称数组。然后循环遍历它们,为每个键创建一个嵌套结构,直到你到达最后。然后用你想要的字段添加元素

标签: javascript arrays tree


【解决方案1】:

这是一个使用嵌套reduce() 调用的解决方案。

const output = input.reduce((a, { LongName, ...attributes }) => {
  const levels = LongName.split(';');
  
  const lastLevel = levels.pop();

  innerChildArray = levels.reduce((b, levelName) => {
    let levelIndex = b.findIndex(({ Name }) => Name === levelName);
    if (levelIndex === -1) {
      levelIndex = b.push({ Name: levelName, Children: [] }) - 1;
    }
    return b[levelIndex].Children;
  }, a);

  innerChildArray.push({ Name: lastLevel, ...attributes })

  return a;
}, []);

console.log(JSON.stringify(output, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script>
const input = [ { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" }, { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" }, { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" }, { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" }, { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" }, { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" }, { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" }, { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" }, { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" }, { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" }, { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" },];
</script>

【讨论】:

    【解决方案2】:
    <script>
        var input = [
            { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
            { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
            { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
            { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
            { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
            { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
            { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
            { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
            { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
            { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
            { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" }
        ];
        let output = [];
        input.forEach((item) => {
            let names = item.LongName.split(';');
            let records = output.filter((rec) => {
                return rec.Name == names[0];
            });
            let rec = { Name: names[0], Children: [] };
            if (records.length > 0) rec = records[0];
            else output.push(rec);
            let childs = rec.Children.filter((rec) => {
                return rec.Name == names[1];
            });
            let child = { Name: names[1], Children: [] };
            if (childs.length > 0) child = childs[0];
            else rec.Children.push(child);
            child.Children.push({ Name: names[2], Id: item.Id, Attribute1: item.Attribute1 });
        });
        console.log(output);
    </script>
    

    output of above program

    【讨论】:

      【解决方案3】:

      使用forEach循环遍历
      在每个项目上,将名称拆分为;
      检查要推送的适当数组(父数组或子数组)。 (使用ptr_arr

      const process = (input, output = []) => {
        input.forEach(({LongName, ...rest}) => {
          const keys = LongName.split(";");
          let ptr_arr = output;
      
          while (keys.length > 0) {
            let key = keys.shift();
            if (keys.length === 0) {
              // its leaf
              ptr_arr.push({ Name: key, ...rest });
            } else {
              let index = ptr_arr.findIndex(({ Name }) => Name === key);
              if (index === -1) {
                ptr_arr.push({ Name: key, Children: [] });
                index = ptr_arr.length - 1;
              }
              ptr_arr = ptr_arr[index].Children;
            }
          }
        });
        return output;
      };
      
      var input = [
        { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
        { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
        { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
        { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
        { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
        { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
        { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
        { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
        { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
        { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
        {
          Id: 11,
          LongName: "North America;US;New York",
          Attribute1: "some attribute",
        },
      ];
      
      
      
      console.log(process(input))

      【讨论】:

        【解决方案4】:

        var input = [
            { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
            { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
            { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
            { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
            { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
            { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
            { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
            { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
            { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
            { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
            { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" },
        ];
        
        output = input.reduce((result, item) => 
          { 
            let g = item.LongName.split(';'), node1, node2, node3;  
        
            node1 = result.find         ( ({Name}) => Name === g[0] ); if (node1)
            node2 = node1.Children.find ( ({Name}) => Name === g[1] ); if (node2)
            node3 = node2.Children.find ( ({Name}) => Name === g[2] );
        
            if (node1 == undefined) result.push ( { Name: g[0], Children: [ { Name: g[1], Children: [ { Name: g[2], Id: item.Id, Attribute1: item.Attribute1 } ] } ] } )
            else
            if (node2 == undefined) node1.Children.push ( { Name: g[1], Children: [ { Name: g[2], Id: item.Id, Attribute1: item.Attribute1 } ] } )
            else
            if (node3 == undefined) node2.Children.push ( { Name: g[2], Id: item.Id, Attribute1: item.Attribute1 } )
            else
            console.log(item.LongName +' path already exists');
            
            return result;
          }, []
          );
          
          console.log(output);

        【讨论】:

        • 谢谢理查德,它有效。我更喜欢 pilchard 的方案,因为它也适用于不同级别的节点,更能适应其他场景。
        【解决方案5】:

        这是@pilchards 答案的扩展版本,它将处理LongName 值指定各种级别名称的情况。

        在此示例中,ID 412 ... 415 是一个 4 级 LongName 嵌套在之前添加的 New York 叶下方。我还更改了一些变量名称以更能说明它们的作用。

        // Generic way
        
        var input = [
            { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
            { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
            { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
            { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
            { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
            { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
            { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
            { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
            { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
            { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
            { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" },
            { Id: 412, LongName: "North America;US;New York;County1", Attribute1: "some attribute" },
            { Id: 413, LongName: "North America;US;New York;County2", Attribute1: "some attribute" },
            { Id: 414, LongName: "North America;US;New York;County3", Attribute1: "some attribute" },
            { Id: 415, LongName: "North America;US;New York;County3", Attribute1: "some attribute" },
        ];
        
        output = input.reduce((rootChildren, { LongName, ...attributes }) => { 
            const levelnames = LongName.split(';');
                const leafname = levelnames.pop();
            
            // descend the tree to the array containing the leafs. insert levels as needed
            const bottomChildren = levelnames.reduce( (children, levelName) => {
            
              let levelIndex = children.findIndex ( ({Name}) => Name === levelName);
              
              if (levelIndex === -1) { // add new level at end of children
                levelIndex = children.push ({ Name: levelName, Children:[] }) - 1;
              } else
              if (!children[levelIndex].hasOwnProperty("Children")) {  // add Children to existing node
                children[levelIndex].Children = [];
              }
              
              return children[levelIndex].Children; // descend
            }, rootChildren);
            
            // add the leaf
            bottomChildren.push({ Name: leafname, ...attributes });
            
            return rootChildren;    
        }, []);
          
        console.log(JSON.stringify(output,null,2));

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-10-11
          • 2013-07-26
          • 1970-01-01
          • 2011-10-30
          • 2020-01-15
          • 2015-07-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多