从 objects 的数组和 javascript 中的维度列表创建 parent child 结构

Create parent child structure from array of objects and list of dimension in javascript

我从 mysql table 获得了按州、城市和产品分组的销售数据。 我使用波纹管查询从 MySql table

获取数据
select state,city,product,sales from salesTable group by state,city,product;

并从查询中得到以下输出,

[
  {
    "state": "S1",
    "city": "CITY1",
    "product": "P1",
    "sales": 1000
  },
  {
    "state": "S1",
    "city": "CITY2",
    "product": "P1",
    "sales": 2000
  },
  {
    "state": "S1",
    "city": "CITY1",
    "product": "P2",
    "sales": 2000
  },
  {
    "state": "S2",
    "city": "CITY1",
    "product": "P1",
    "sales": 1000
  },
  {
    "state": "S2",
    "city": "CITY2",
    "product": "P1",
    "sales": 2000
  },
  {
    "state": "S2",
    "city": "CITY2",
    "product": "P2",
    "sales": 2000
  },
  {
    "state": "S3",
    "city": "CITY1",
    "product": "P2",
    "sales": 1000
  },
  {
    "state": "S3",
    "city": "CITY2",
    "product": "P2",
    "sales": 2000
  }
]

现在我想从 dimensions=["state","city","product"] 创建 parent child 结构 其中州为盛大 parent,城市为 parent(州的 child),产品为 child.

维度数组应该是动态的,它的长度可能会增加或减少。

我需要波纹管输出,

[
  {
    "sales": 5000,
    "state": "S1",
    "children": [
      {
        "sales": 3000,
        "state": "S1",
        "city": "CITY1",
        "children": [
          {
            "sales": 1000,
            "state": "S1",
            "city": "CITY1",
            "product": "P1"
          },
          {
            "sales": 2000,
            "state": "S1",
            "city": "CITY1",
            "product": "P2"
          }
        ]
      },
      {
        "sales": 2000,
        "state": "S1",
        "city": "CITY2",
        "children": [
          {
            "sales": 2000,
            "state": "S1",
            "city": "CITY2",
            "children": [
              {
                "sales": 2000,
                "state": "S1",
                "city": "CITY2",
                "product": "P1"
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "sales": 5000,
    "state": "S2",
    "children": [
      {
        "sales": 1000,
        "state": "S2",
        "city": "CITY1",
        "children": [
          {
            "sales": 1000,
            "state": "S2",
            "city": "CITY1",
            "product": "P1"
          }
        ]
      },
      {
        "sales": 4000,
        "state": "S2",
        "city": "CITY2",
        "children": [
          {
            "sales": 4000,
            "state": "S2",
            "city": "CITY2",
            "children": [
              {
                "sales": 2000,
                "state": "S2",
                "city": "CITY2",
                "product": "P1"
              },
              {
                "sales": 2000,
                "state": "S2",
                "city": "CITY2",
                "product": "P2"
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "sales": 3000,
    "state": "S3",
    "children": [
      {
        "sales": 1000,
        "state": "S3",
        "city": "CITY1",
        "children": [
          {
            "sales": 1000,
            "state": "S3",
            "city": "CITY1",
            "product": "P2"
          }
        ]
      },
      {
        "sales": 2000,
        "state": "S3",
        "city": "CITY2",
        "children": [
          {
            "sales": 2000,
            "state": "S3",
            "city": "CITY2",
            "children": [
              {
                "sales": 2000,
                "state": "S3",
                "city": "CITY2",
                "product": "P2"
              }
            ]
          }
        ]
      }
    ]
  }
]

也许是这样的。在第一次迭代中,我们使用时间对象而不是数组来构建树,以便于分发。在第二次递归迭代中,我们从时间对象创建数组并计算销售额。

为了统一,最顶层也使用.children键及其.sales总和。这可以通过在末尾使用 result.children 而不是 result 来忽略。

const data = [
  { state: 'S1', city: 'CITY1', product: 'P1', sales: 1000 },
  { state: 'S1', city: 'CITY2', product: 'P1', sales: 2000 },
  { state: 'S1', city: 'CITY1', product: 'P2', sales: 2000 },
  { state: 'S2', city: 'CITY1', product: 'P1', sales: 1000 },
  { state: 'S2', city: 'CITY2', product: 'P1', sales: 2000 },
  { state: 'S2', city: 'CITY2', product: 'P2', sales: 2000 },
  { state: 'S3', city: 'CITY1', product: 'P2', sales: 1000 },
  { state: 'S3', city: 'CITY2', product: 'P2', sales: 2000 },
];

const dimensions = ['state', 'city', 'product'];
const childKey = dimensions[dimensions.length - 1];

const result = { children: Object.create(null) };

for (const entry of data) {
  let parrent = null;
  let current = result.children;

  for (const dimension of dimensions) {
    let slot = current[entry[dimension]];
    if (!slot) {
      slot = current[entry[dimension]] = Object.create(null);
      slot.sales = dimension === childKey ? entry.sales : 0;

      if (parrent) {
        for (const [k, v] of Object.entries(parrent)) {
          if (k !== 'children' && k !== 'sales') slot[k] = v;
        }
      }

      slot[dimension] = entry[dimension];

      if (dimension !== childKey) {
        slot.children = Object.create(null);
      }
    }

    parrent = slot;
    current = slot.children;
  }
}

normalizeAndSum(result, null);

console.log(JSON.stringify(result, null, '  '));

function normalizeAndSum(object, parent) {
  if (object.children) {
    object.children = Object.values(object.children);
    for (const child of object.children) normalizeAndSum(child, object);
  }
  if (parent) {
    parent.sales = parent.children.reduce((acc, { sales }) => acc + sales, 0);
  }
}