将平面的对象数组转换为嵌套的对象数组

Convert flat array of objects into nested array of objects

本文关键字:数组 对象 嵌套 转换 平面      更新时间:2023-09-26

原始JSON数据(平面表):

[
    {"id":"1","first_name":"Jason","last_name":"Martin","start_date":"1996-07-25","end_date":"2006-07-25","salary":"1234.56","city":"Toronto","description":"Programmer","department":"Finance","active":"1"},
    {"id":"2","first_name":"Alison","last_name":"Mathews","start_date":"1976-03-21","end_date":"1986-02-21","salary":"6661.78","city":"Vancouver","description":"Tester","department":"Finance","active":"1"},
    {"id":"3","first_name":"James","last_name":"Smith","start_date":"1978-12-12","end_date":"1990-03-15","salary":"6544.78","city":"Vancouver","description":"Tester","department":"QA","active":"1"},
    {"id":"4","first_name":"Celia","last_name":"Rice","start_date":"1982-10-24","end_date":"1999-04-21","salary":"2344.78","city":"Vancouver","description":"Manager","department":"HR","active":"1"},
    {"id":"5","first_name":"Robert","last_name":"Black","start_date":"1984-01-15","end_date":"1998-08-08","salary":"2334.78","city":"Vancouver","description":"Tester","department":"IT","active":"1"},
    {"id":"6","first_name":"Linda","last_name":"Green","start_date":"1987-07-30","end_date":"1996-01-04","salary":"4322.78","city":"New York","description":"Tester","department":"QA","active":"1"},
    {"id":"7","first_name":"David","last_name":"Larry","start_date":"1990-12-31","end_date":"1998-02-12","salary":"7897.78","city":"New York","description":"Manager","department":"HR","active":"1"}
]

我需要像这样调用函数:

nest(data,["city","description","department"])

第一个参数是整个数据集,第二个参数是定义嵌套级别的列数组。

期望JSON输出:

[
{key: "city", value: "Toronto", count: 1, children:
    [
        {key: "description", value: "Programmer", count: 1, children:
            [
                {key: "department", value: "Finance", count: 1}
            ]
        }
    ]
},
{key: "city", value: "Vancouver", count: 2, children: 
    [
        {key: "description", value: "Tester", count: 3, children:
            [
                {key: "department", value: "Finance", count: 1},
                {key: "department", value: "QA", count: 1},
                {key: "department", value: "IT", count: 1}
            ]
        },
        {key: "description", value: "Manager", count: 1}
    ]
},
{key: "city", value: "New York", count: 2, children:
    [
        {key: "description", value: "Tester", count: 1, children:
            [
                {key: "department", value: "QA", count: 1}
            ]
        },
        {key: "description", value: "Manager", count: 1, children:
            [
                {key: "department", value: "HR", count: 1}
            ]
        }
    ]
}

)

我试过写一些递归函数,但当我必须动态搜索树以避免重复时,总是卡住。

认为这是一个有趣的小问题,所以我做了…但是,我确实同意那些问"你迄今为止尝试过什么"的人。通常,您应该谈论一个特定的问题。

// Groups a flat array into a tree. 
// "data" is the flat array.
// "keys" is an array of properties to group on.
function groupBy(data, keys) { 
    if (keys.length == 0) return data;
    // The current key to perform the grouping on:
    var key = keys[0];
    // Loop through the data and construct buckets for
    // all of the unique keys:
    var groups = {};
    for (var i = 0; i < data.length; i++)
    {
        var row = data[i];
        var groupValue = row[key];
        if (groups[groupValue] == undefined)
        {
            groups[groupValue] = new Array();
        }
        groups[groupValue].push(row);
    }
    // Remove the first element from the groups array:
    keys.reverse();
    keys.pop()
    keys.reverse();
    // If there are no more keys left, we're done:
    if (keys.length == 0) return groups;
    // Otherwise, handle further groupings:
    for (var group in groups)
    {
        groups[group] = groupBy(groups[group], keys.slice());
    }
    return groups;
}

像这样调用方法:

var groupedData = groupBy(data, ["city","description","department"]);

该方法对数据的输出如下所示:

{
    "Toronto": {
        "Programmer": {
            "Finance": [
                {
                    "id": "1", "first_name": "Jason", "last_name": "Martin", "start_date": "1996-07-25", "end_date": "2006-07-25", "salary": "1234.56", "city": "Toronto", "description": "Programmer", "department": "Finance", "active": "1"
                }
            ]
        }
    },
    "Vancouver": {
        "Tester": {
            "Finance": [
                {
                    "id": "2", "first_name": "Alison", "last_name": "Mathews", "start_date": "1976-03-21", "end_date": "1986-02-21", "salary": "6661.78", "city": "Vancouver", "description": "Tester", "department": "Finance", "active": "1"
                }
            ],
            "QA": [
                {
                    "id": "3", "first_name": "James", "last_name": "Smith", "start_date": "1978-12-12", "end_date": "1990-03-15", "salary": "6544.78", "city": "Vancouver", "description": "Tester", "department": "QA", "active": "1"
                }
            ],
            "IT": [
                {
                    "id": "5", "first_name": "Robert",  "last_name": "Black", "start_date": "1984-01-15", "end_date": "1998-08-08", "salary": "2334.78", "city": "Vancouver", "description": "Tester", "department": "IT", "active": "1"
                }
            ]
        },
        "Manager": {
            "HR": [
                {
                    "id": "4", "first_name": "Celia", "last_name": "Rice", "start_date": "1982-10-24", "end_date": "1999-04-21", "salary": "2344.78", "city": "Vancouver", "description": "Manager", "department": "HR", "active": "1"
                }
            ]
        }
    },
    "New York": {
        "Tester": {
            "QA": [
                {
                    "id": "6", "first_name": "Linda", "last_name": "Green", "start_date": "1987-07-30", "end_date": "1996-01-04", "salary": "4322.78", "city": "New York", "description": "Tester", "department": "QA", "active": "1"
                }
            ]
        },
        "Manager": {
            "HR": [
                {
                    "id": "7", "first_name": "David", "last_name": "Larry", "start_date": "1990-12-31", "end_date": "1998-02-12", "salary": "7897.78", "city": "New York", "description": "Manager", "department": "HR", "active": "1"
                }
            ]
        }
    }
}

因为组都是javascript对象,所以不需要"count"成员。您可以简单地使用数组的.length属性。

使用javascript的for (var group in groups)语法遍历组

你可以看看D3.js中的nest()操作符:https://github.com/mbostock/d3/blob/48ad44fdeef32b518c6271bb99a9aed376c1a1d6/src/arrays/nest.js这是D3的一部分,一个更大的库,但快速查看我刚刚链接到的代码,我不认为这有任何依赖关系,所以你应该能够在这里解除代码用于你自己的项目。用法在这里的文档中描述-您链接.key()方法来定义嵌套结构的每一层的键。在您的示例中,这可能看起来像:

data = d3.nest()
    .key(function(d) { return d.city })
    .key(function(d) { return d.description })
    .entries(data);

生成的结构与您的结构略有不同,但功能非常相似:

[
  {
    "key": "Toronto", 
    "values": [
      {
        "key": "Programmer", 
        "values": [
          {
            "active": "1", 
            "city": "Toronto", 
            "department": "Finance", 
            "description": "Programmer", 
            "end_date": "2006-07-25", 
            "first_name": "Jason", 
            "id": "1", 
            "last_name": "Martin", 
            "salary": "1234.56", 
            "start_date": "1996-07-25"
          },
          // etc ...
        ]
      }
    ]
  },
  // etc ...
]

以@nrabinowitz提供的示例为基础,下面是嵌套函数,使用最初提出的API,将集合和属性名称数组作为参数传递,使用d3。巢在引擎盖下:

function nest(data, keys) {
  var nest = d3.nest();
  keys.forEach(function(k) { 
    nest.key(function(d) {
      return d[k];
    })
  });
  return nest.entries(data);
}

使用https://www.npmjs.com/package/nesthydrationjs

const NestHydrationJS = require('nesthydrationjs')();
function getNestHydrationJS (results) {
  return  NestHydrationJS.nest(results);
}