js求数据格式转化?

// 按照顺序的要进行统计的字段,是动态的,它们是值为非数字的字段
// 如果这里加上quater那就得再加一层季度的统计数据
let key = ['company','bank'] 
// 后端传来的数据
let tableData = [
  {id:1,bank:'中国银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:2,bank:'中国银行',company:'广东公司',quater:'第二季度',money:200,fee:300},
  {id:3,bank:'中国银行',company:'湖南公司',quater:'第一季度',money:300,fee:400},
  {id:4,bank:'中国银行',company:'湖南公司',quater:'第二季度',money:400,fee:500},
  {id:5,bank:'广发银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:6,bank:'广发银行',company:'广东公司',quater:'第二季度',money:500,fee:600},
  {id:7,bank:'广发银行',company:'湖南公司',quater:'第一季度',money:600,fee:700},
  {id:8,bank:'广发银行',company:'湖南公司',quater:'第二季度',money:600,fee:700},
]
// 根据字段顺序做映射
let map = {
    "广东公司": {
        "children": {
            "中国银行": {
                "children": [
                     {id:1,bank:'中国银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
                     {id:2,bank:'中国银行',company:'广东公司',quater:'第二季度',money:200,fee:300},
                ],
                "total":{bank:'中国银行合计',company:'',quater:'',money:300,fee:500}
            },
            "广发银行": {
                "children": [
                    {id:5,bank:'广发银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
                    {id:6,bank:'广发银行',company:'广东公司',quater:'第二季度',money:500,fee:600},
                ],
                "total":{bank:'广发银行合计',company:'',quater:'',money:600,fee:800}
            }
        },
        "total":{bank:'',company:'广东公司合计',quater:'',money:900,fee:1300}
    },
    "湖南公司": {
        "children": {
            "中国银行": {
                "children": [
                     {id:3,bank:'中国银行',company:'湖南公司',quater:'第一季度',money:300,fee:400},
                     {id:4,bank:'中国银行',company:'湖南公司',quater:'第二季度',money:400,fee:500},
                ],
                "total":{bank:'中国银行合计',company:'',quater:'',money:700,fee:900}
            },
            "广发银行": {
                "children": [
                     {id:7,bank:'广发银行',company:'湖南公司',quater:'第一季度',money:600,fee:700},
                     {id:8,bank:'广发银行',company:'湖南公司',quater:'第二季度',money:600,fee:700},
                ],
                "total":{bank:'广发银行合计',company:'',quater:'',money:1200,fee:1400}
            }
        },
        "total":{bank:'',company:'湖南公司合计',quater:'',money:1900,fee:2300}
    }
}
// 处理后的要渲染到表格的数据
let finalData = [
    {id:1,company:'广东公司',    bank:'中国银行',   quater:'第一季度',money:100,fee:200},
    {id:2,company:'',           bank:'',           quater:'第二季度',money:200,fee:300},
    {     company:'',           bank:'中国银行合计',quater:'',        money:300,fee:500},

    {id:5,company:'',           bank:'广发银行',    quater:'第一季度',money:100,fee:200},
    {id:6,company:'',           bank:'',           quater:'第二季度',money:500,fee:600},
    {     company:'',           bank:'广发银行合计',quater:'',       money:600,fee:800},

    {     company:'广东公司合计',bank:'',           quater:'',       money:900,fee:1300},

    {id:3,company:'湖南公司',    bank:'中国银行',   quater:'第一季度',money:300,fee:400},
    {id:4,company:'',           bank:'',           quater:'第二季度',money:400,fee:500},
    {     company:'',           bank:'中国银行合计',quater:'',       money:700,fee:900},

    {id:7,company:'',           bank:'广发银行',   quater:'第一季度',money:600,fee:700},
    {id:8,company:'',           bank:'',          quater:'第二季度',money:600,fee:700},
    {     company:'',           bank:'广发银行合计',quater:'',       money:1200,fee:1400},

    {     company:'湖南公司合计',bank:'',           quater:'',       money:1900,fee:2300}
]

请问怎么根据key和tableData获得finalData?我目前想到使用map做映射,但是代码怎么写给我难住了。
https://codepen.io/hongsir12/...

问题补充:

如果小计字段是按['bank','company','quater']排序 ,那么需要最后处理得到的数据如下:

const tableData = [
  {id:1,bank:'中国银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:2,bank:'中国银行',company:'广东公司',quater:'第二季度',money:200,fee:300},
  {id:15,bank:'中国银行',company:'广东公司',quater:'第二季度',money:200,fee:300},
  {id:3,bank:'中国银行',company:'湖南公司',quater:'第一季度',money:300,fee:400},
  {id:4,bank:'中国银行',company:'湖南公司',quater:'第二季度',money:400,fee:500},
  {id:5,bank:'广发银行',company:'武汉公司',quater:'第三季度',money:400,fee:500},
  {id:6,bank:'广发银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:7,bank:'广发银行',company:'广东公司',quater:'第二季度',money:500,fee:600},
  {id:8,bank:'广发银行',company:'湖南公司',quater:'第一季度',money:600,fee:700},
  {id:9,bank:'广发银行',company:'湖南公司',quater:'第二季度',money:600,fee:700},
  {id:10,bank:'建设银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:11,bank:'建设银行',company:'广东公司',quater:'第二季度',money:500,fee:600},
  {id:12,bank:'建设银行',company:'湖南公司',quater:'第一季度',money:600,fee:700},
  {id:13,bank:'建设银行',company:'湖南公司',quater:'第二季度',money:600,fee:700},
  {id:14,bank:'建设银行',company:'湖南公司',quater:'第三季度',money:600,fee:700},
]
// 处理后的数据:
// 要求每一级别相同的值进行单元格合并,而为此该级别的字段的值只能出现一次
const  finalData = [
    {"id": 1,"bank": "中国银行","company": "广东公司","quater": "第一季度","money": 100,"fee": 200},
    {        "bank": "",        "company": "",       "quater": "第一季度小计","money": 100,"fee": 200},
    {"id": 2,"bank": "",        "company": "",        "quater": "第二季度","money": 200,"fee": 300},
    {"id": 15,"bank": "",       "company": "",        "quater": "",        "money": 200,"fee": 300},
    {         "bank": "",       "company": "",        "quater": "第二季度小计","money": 400,"fee": 600},
    {         "bank": "",        "company": "广东公司小计","quater": "","money": 500,"fee": 800},
    {"id": 3,"bank": "",         "company": "湖南公司",  "quater": "第一季度","money": 300,"fee": 400},
    {        "bank": "",         "company": "",         "quater": "第一季度小计","money": 300,"fee": 400},
    {"id": 4,"bank": "",         "company": "",          "quater": "第二季度","money": 400,"fee": 500},
    {        "bank": "",         "company": "",         "quater": "第二季度小计","money": 400,"fee": 500},
    {        "bank": "",         "company": "湖南公司小计","quater": "","money": 700,"fee": 900},
    {        "bank": "中国银行小计","company": "",         "quater": "","money": 1200,"fee": 1700},
    {"id": 6,"bank": "广发银行",  "company": "广东公司",   "quater": "第一季度","money": 100,"fee": 200},
    {        "bank": "",          "company": "",           "quater": "第一季度小计","money": 100,"fee": 200},
    {"id": 7,"bank": "",          "company": "",            "quater": "第二季度","money": 500,"fee": 600},
    {        "bank": "",          "company": "",            "quater": "第二季度小计","money": 500,"fee": 600},
    {        "bank": "",          "company": "广东公司小计","quater": "","money": 600,"fee": 800},
    {"id": 8, "bank": "",         "company": "湖南公司",    "quater": "第一季度","money": 600,"fee": 700},
    {        "bank": "",          "company": "",           "quater": "第一季度小计","money": 600,"fee": 700},
    { "id": 9,"bank": "",         "company": "",             "quater": "第二季度","money": 600,"fee": 700},
    {         "bank": "",         "company": "",            "quater": "第二季度小计","money": 600,"fee": 700},
    {         "bank": "",         "company": "湖南公司小计", "quater": "","money": 1200,"fee": 1400},
    {"id": 5,"bank": "",          "company": "武汉公司",     "quater": "第三季度", "money": 400,"fee": 500},
    {        "bank": "",          "company": "",             "quater": "第三季度小计", "money": 400,"fee": 500},
    {        "bank": "",          "company": "武汉公司小计",  "quater": "","money": 400,"fee": 500},
    {        "bank": "广发银行小计","company": "",            "quater": "","money": 2200,"fee": 2700},
    {"id": 10, "bank": "建设银行",  "company": "广东公司",    "quater": "第一季度","money": 100, "fee": 200},
    {         "bank": "",          "company": "",            "quater": "第一季度小计","money": 100,"fee": 200},
    {"id": 11,"bank": "",          "company": "",            "quater": "第二季度","money": 500,"fee": 600},
    {         "bank": "",          "company": "",            "quater": "第二季度小计","money": 500,"fee": 600},
    {         "bank": "",          "company": "广东公司小计",  "quater": "","money": 600,"fee": 800},
    {"id": 12,"bank": "",          "company": "湖南公司",      "quater": "第一季度", "money": 600,"fee": 700},
    {         "bank": "",          "company": "",              "quater": "第一季度小计", "money": 600,"fee": 700},
    {"id": 13,"bank": "",          "company": "",              "quater": "第二季度", "money": 600, "fee": 700 },
    {         "bank": "",          "company": "",               "quater": "第二季度小计", "money": 600,"fee": 700},
    {"id": 14,"bank": "",          "company": "",               "quater": "第三季度", "money": 600,"fee": 700},
    {         "bank": "",           "company": "",               "quater": "第三季度小计","money": 600,"fee": 700},
    {         "bank": "",            "company": "湖南公司小计",   "quater": "","money": 1800,"fee": 2100},
    {         "bank": "建设银行小计","company": "",                "quater": "","money": 2400,"fee": 2900}
]
阅读 1.7k
3 个回答

分组求和递归版,输出扁平列表
看你代码,是分成两步处理,其实也可以合成一步
参考代码:

const subtotalFields = ["bank", "company"];
const quatoFields = ["money", "fee"];
const res = [];
const subtotal = (data, i = 0) => {
  const total = Object.fromEntries(quatoFields.map((k) => [k, 0]));
  if (i == subtotalFields.length) {
    res.push(...data);
    data.forEach((item) => quatoFields.forEach((k) => (total[k] += item[k])));
  } else {
    const k = subtotalFields[i++];
    Object.entries(
      data.reduce((o, item) => ((o[item[k]] ??= []).push(item), o), {})
    ).forEach(([feature, items]) => {
      const sub = subtotal(items, i);
      res.push({ [k]: feature + "小计", ...sub });
      quatoFields.forEach((k) => (total[k] += sub[k]));
    });
  }
  return total;
};

const total = subtotal(tableData);
console.log(res);
console.log("总计", total);

重构了一下函数

主要改了分组代码,如果不是组内第一个 item 则删除该 item 的分组字段

(o, item) => ((o[item[k]] ??= []).push(item), o)

改为

(o, item) => {
  const feature = item[k];
  if (feature in o) {
    delete item[k];
  } else {
    o[feature] = [];
  }
  o[feature].push(item);
  return o;
}

完整代码:

function subtotal({ data, subtotalFields, quatoFields }) {
  const res = [];
  const total = (function dfs(items, i) {
    const total = Object.fromEntries(quatoFields.map((k) => [k, 0]));
    if (i == subtotalFields.length) {
      res.push(...items);
      items.forEach((item) => quatoFields.forEach((k) => (total[k] += item[k])));
    } else {
      const k = subtotalFields[i++];
      Object.entries(
        items.reduce((o, item) => {
          const feature = item[k];
          if (feature in o) {
            delete item[k];
          } else {
            o[feature] = [];
          }
          o[feature].push(item);
          return o;
        }, {})
      ).forEach(([feature, items]) => {
        const sub = dfs(items, i);
        res.push({ [k]: feature + "小计", ...sub });
        quatoFields.forEach((k) => (total[k] += sub[k]));
      });
    }
    return total;
  })(data.map((item) => ({ ...item })), 0);
  return [res, total];
}

const [res, total] = subtotal({
  data: tableData,
  subtotalFields: ["bank", "company", "quater"],
  quatoFields: ["money", "fee"],
});

console.log(res);
console.log("总计", total);

如果分组字段是数字,输出顺序可能会乱,因为对象的数字键会被自动排序,这时可以改用 Map 进行分组

Map 对象保存键值对,并且能够记住键的原始插入顺序。

修改后的代码:

function subtotal({ data, subtotalFields, quatoFields }) {
  const res = [];
  const subtotalFieldsObj = Object.fromEntries(subtotalFields.map((item) => [item, ""]));
  const total = (function dfs(items, i) {
    const total = Object.fromEntries(quatoFields.map((k) => [k, 0]));
    if (i == subtotalFields.length) {
      items.forEach((item) => {
        quatoFields.forEach((k) => (total[k] += +item[k]));
      });
      res.push(...items);
    } else {
      const k = subtotalFields[i++];
      items
        .reduce((map, item) => {
          const feature = item[k];
          if (map.has(feature)) {
            item[k] = "";
          } else {
            map.set(feature, []);
          }
          map.get(feature).push(item);
          return map;
        }, new Map())
        .forEach((items, feature) => {
          const sub = dfs(items, i);
          res.push({ ...subtotalFieldsObj, [k]: feature + "小计", ...sub });
          quatoFields.forEach((k) => (total[k] += sub[k]));
        });
    }
    return total;
  })(data.map((item) => ({ ...item, rawItem: item })), 0);
  return [res, total];
}

const [res, total] = subtotal({
  data: tableData,
  subtotalFields: ["bank", "zquarter", "werks"],
  quatoFields: ["paper_money", "deposit"],
});

console.log(res);
console.log(total);
let subtotalFields = ['bank','company'] // 开启小计的字段
let featureFields = ['bank','company','quater'] // 特征字段
featureFieldsObj = Object.fromEntries(featureFields.map(item=>[item,'']))
// console.log(featureFieldsObj)
let quatoFields = ['money','fee'] // 指标字段
let tableData = [
  {id:1,bank:'中国银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:2,bank:'中国银行',company:'广东公司',quater:'第二季度',money:200,fee:300},
  {id:3,bank:'中国银行',company:'湖南公司',quater:'第一季度',money:300,fee:400},
  {id:4,bank:'中国银行',company:'湖南公司',quater:'第二季度',money:400,fee:500},
  {id:5,bank:'广发银行',company:'武汉公司',quater:'第三季度',money:400,fee:500},
  {id:6,bank:'广发银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:7,bank:'广发银行',company:'广东公司',quater:'第二季度',money:500,fee:600},
  {id:8,bank:'广发银行',company:'湖南公司',quater:'第一季度',money:600,fee:700},
  {id:9,bank:'广发银行',company:'湖南公司',quater:'第二季度',money:600,fee:700},
  {id:10,bank:'建设银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:11,bank:'建设银行',company:'广东公司',quater:'第二季度',money:500,fee:600},
  {id:12,bank:'建设银行',company:'湖南公司',quater:'第一季度',money:600,fee:700},
  {id:13,bank:'建设银行',company:'湖南公司',quater:'第二季度',money:600,fee:700},
  {id:14,bank:'建设银行',company:'湖南公司',quater:'第三季度',money:600,fee:700},
]
const attr = (data,arr,index=0,valueMap = {}) =>{
    let map = {}
    // 索引值等于数组长度,结束递归
    if(index===arr.length){
        return 
    }else{
        // 生成当前级对应索引值字段的值映射
        for(let rec of data){
            map[rec[arr[index]]] ={}
        }
        for(let k in map){
            // 如果索引值到最后一级
            if(index===arr.length-1){
                valueMap[arr[index]] = k
                // console.log(valueMap)
                map[k].children = data.filter((item,index)=>{
                    return arr.every(key=>item[key]==valueMap[key])
                })
                if(map[k].children.length){
                    let keyArr = arr.slice(0,index+1)
                    map[k].total = data.filter(item=>{
                        return keyArr.every(key=>item[key]==valueMap[key])
                    }).reduce((pre,next)=>{
                        quatoFields.forEach(field=>{
                            pre[field] = pre[field]||0
                            pre[field]+=Number(next[field])
                        })
                        return pre
                    },{...featureFieldsObj})
                    map[k].total[arr[index]] = `${k}小计`
                }  
            }else{ // 否则,继续递归子级
                valueMap[arr[index]] = k  // 当前级别的字段对应值映射
                // console.log(valueMap)       
                map[k].children = attr(data,arr,index+1,{...valueMap})
                let keyArr = arr.slice(0,index+1) // 筛选的小计字段依据
                // 筛选当前级别的数据
                map[k].total = data.filter(item=>{
                    return keyArr.every(key=>item[key]==valueMap[key])
                })
                // 计算总计
                if(map[k].total.length){
                    map[k].total =  map[k].total.reduce((pre,next)=>{
                        quatoFields.forEach(field=>{
                            pre[field] = pre[field]||0
                            pre[field]+=Number(next[field])
                        })
                        return pre
                    },{...featureFieldsObj})
                    map[k].total[arr[index]] = `${k}小计`
                }    
            }
        }
        return map
    }
}
let subtotalMap = attr(tableData,subtotalFields)
console.log(subtotalMap)

function handleMap(map){
    let arr = []
    for(let k in map){
        if(Array.isArray(map[k].children)){
            if(map[k].children.length){
                arr.push(...map[k].children)
                arr.push(map[k].total)
            }  
        }else{
            arr.push(...handleMap(map[k].children))
            arr.push(map[k].total)
        }
    }
    return arr
}

console.log(handleMap(subtotalMap))

虽然写出来了,但好像太复杂了这过程。。。

你这个需求是想要实现按照company进行单元格合并 + 增加 合计功能吧。那应该只需要按照company处理下排序,在render的时候去处理显示问题及合计的处理

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题