【问题标题】:Build tree array from flat array in node.js从 node.js 中的平面数组构建树数组
【发布时间】:2016-09-25 14:21:56
【问题描述】:

我有一个平面数组,我想在 node.js 中构建一个树数组。 我的数组是这样的:

var data=  [
        {
            "id1": 1001,
            "id2": 1002,
            "id3": 1004,
            "id4": 1007,
            "id5": 1013,
            "id6": 1030,
            "id7": null,
            "id8": null,
            "id9": null,
            "accountid": 1108,
            "name1": "Net Income",
            "name2": "Net Income Before Extraordinary Item",
            "name3": "Income (Loss) from operations                     ",
            "name4": "Net Patient Revenue",
            "name5": "Gross Patient Revenue",
            "name6": "Inpatient Routine Services",
            "name7": null,
            "name8": null,
            "name9": null,
            "accountname": "3000000 - INPATIENT REVENUE                       ",
            "amt1": 6266235,
            "amt2": 0,
            "amt3": 7085312,
            "amt4": 7010901,
            "amt5": 7008743,
            "amt6": 6865373,
            "amt7": 7298176,
            "amt8": 7481711,
            "nCurMnth": 0,
            "samt4": 7010901,
            "id": 1030,
            "parentid": 1013,
            "relativelevel": 6,
            "accountdetailid": null,
            "variancecalc": 1,
            "InfoSetID": 1108,
            "InfoSetName": "3000000 - INPATIENT REVENUE                       ",
            "DetailType": "",
            "DecimalCount": 2
        },
        {
            "id1": 1001,
            "id2": 1002,
            "id3": 1004,
            "id4": 1007,
            "id5": 1013,
            "id6": 1030,
            "id7": null,
            "id8": null,
            "id9": null,
            "accountid": 2708,
            "name1": "Net Income",
            "name2": "Net Income Before Extraordinary Item",
            "name3": "Income (Loss) from operations                     ",
            "name4": "Net Patient Revenue",
            "name5": "Gross Patient Revenue",
            "name6": "Inpatient Routine Services",
            "name7": null,
            "name8": null,
            "name9": null,
            "accountname": "3000090 - INPATIENT SWING BED                     ",
            "amt1": 190887,
            "amt2": 0,
            "amt3": 256581,
            "amt4": 271789,
            "amt5": 235998,
            "amt6": 251224,
            "amt7": 307154,
            "amt8": 314971,
            "nCurMnth": 0,
            "samt4": 271789,
            "id": 1030,
            "parentid": 1013,
            "relativelevel": 6,
            "accountdetailid": null,
            "variancecalc": 1,
            "InfoSetID": 2708,
            "InfoSetName": "3000090 - INPATIENT SWING BED                     ",
            "DetailType": "",
            "DecimalCount": 2
        },
        {
            "id1": 1001,
            "id2": 1002,
            "id3": 1004,
            "id4": 1007,
            "id5": 1013,
            "id6": 1030,
            "id7": null,
            "id8": null,
            "id9": null,
            "accountid": 1114,
            "name1": "Net Income",
            "name2": "Net Income Before Extraordinary Item",
            "name3": "Income (Loss) from operations                     ",
            "name4": "Net Patient Revenue",
            "name5": "Gross Patient Revenue",
            "name6": "Inpatient Routine Services",
            "name7": null,
            "name8": null,
            "name9": null,
            "accountname": "3999000 - SUSPENSE DAILY REV CLEAR ACCT           ",
            "amt1": 0,
            "amt2": 0,
            "amt3": 0,
            "amt4": 0,
            "amt5": 0,
            "amt6": 0,
            "amt7": 0,
            "amt8": 0,
            "nCurMnth": 0,
            "samt4": 0,
            "id": 1030,
            "parentid": 1013,
            "relativelevel": 6,
            "accountdetailid": null,
            "variancecalc": 1,
            "InfoSetID": 1114,
            "InfoSetName": "3999000 - SUSPENSE DAILY REV CLEAR ACCT           ",
            "DetailType": "",
            "DecimalCount": 2
        },
        {
            "id1": 1001,
            "id2": 1002,
            "id3": 1004,
            "id4": 1007,
            "id5": 1013,
            "id6": 1031,
            "id7": null,
            "id8": null,
            "id9": null,
            "accountid": 1133,
            "name1": "Net Income",
            "name2": "Net Income Before Extraordinary Item",
            "name3": "Income (Loss) from operations                     ",
            "name4": "Net Patient Revenue",
            "name5": "Gross Patient Revenue",
            "name6": "Inpatient Ancillary Services",
            "name7": null,
            "name8": null,
            "name9": null,
            "accountname": "3070000 - INTERP - INP                            ",
            "amt1": 36886,
            "amt2": 0,
            "amt3": 47968,
            "amt4": 45109,
            "amt5": 38047,
            "amt6": 39158,
            "amt7": 31290,
            "amt8": 45148,
            "nCurMnth": 0,
            "samt4": 45109,
            "id": 1031,
            "parentid": 1013,
            "relativelevel": 6,
            "accountdetailid": null,
            "variancecalc": 1,
            "InfoSetID": 1133,
            "InfoSetName": "3070000 - INTERP - INP                            ",
            "DetailType": "",
            "DecimalCount": 2
        },
        {
            "id1": 1001,
            "id2": 1002,
            "id3": 1004,
            "id4": 1007,
            "id5": 1013,
            "id6": 1031,
            "id7": null,
            "id8": null,
            "id9": null,
            "accountid": 1135,
            "name1": "Net Income",
            "name2": "Net Income Before Extraordinary Item",
            "name3": "Income (Loss) from operations                     ",
            "name4": "Net Patient Revenue",
            "name5": "Gross Patient Revenue",
            "name6": "Inpatient Ancillary Services",
            "name7": null,
            "name8": null,
            "name9": null,
            "accountname": "3100000 - INPATIENT ANCILLARY REV                 ",
            "amt1": 18822593,
            "amt2": 0,
            "amt3": 21676463,
            "amt4": 21368866,
            "amt5": 20284449,
            "amt6": 21344632,
            "amt7": 20272660,
            "amt8": 21169123,
            "nCurMnth": 0,
            "samt4": 21368866,
            "id": 1031,
            "parentid": 1013,
            "relativelevel": 6,
            "accountdetailid": null,
            "variancecalc": 1,
            "InfoSetID": 1135,
            "InfoSetName": "3100000 - INPATIENT ANCILLARY REV                 ",
            "DetailType": "",
            "DecimalCount": 2
        },
        {
            "id1": 1001,
            "id2": 1002,
            "id3": 1004,
            "id4": 1007,
            "id5": 1013,
            "id6": 1031,
            "id7": null,
            "id8": null,
            "id9": null,
            "accountid": 1273,
            "name1": "Net Income",
            "name2": "Net Income Before Extraordinary Item",
            "name3": "Income (Loss) from operations                     ",
            "name4": "Net Patient Revenue",
            "name5": "Gross Patient Revenue",
            "name6": "Inpatient Ancillary Services",
            "name7": null,
            "name8": null,
            "name9": null,
            "accountname": "3100090 - SWING BED INPATIENT REV                 ",
            "amt1": 166993,
            "amt2": 0,
            "amt3": 225651,
            "amt4": 228349,
            "amt5": 143726,
            "amt6": 227736,
            "amt7": 235705,
            "amt8": 206381,
            "nCurMnth": 0,
            "samt4": 228349,
            "id": 1031,
            "parentid": 1013,
            "relativelevel": 6,
            "accountdetailid": null,
            "variancecalc": 1,
            "InfoSetID": 1273,
            "InfoSetName": "3100090 - SWING BED INPATIENT REV                 ",
            "DetailType": "",
            "DecimalCount": 2
        }
    ]

注意:- id1 是根,其名称由 name1 表示,id2 是 id1 的直接子代,其名称为 name2,同样 id3 是 id2 的直接子代,其名称为 name3,依此类推。

现在我想要一个由 node.js 中的名称表示的分层(树状)结构。

我也想知道有没有可以做到这一点的包。

提前致谢。

树形的预期输出如下:

Net Income
   |
   ---------Net Income Before Extraordinary Item
            |
            ------Income (Loss) from operations
            |     |
            |     ------Net Patient Revenue   
            |           |
            |            --------- 
            ------XYZ

【问题讨论】:

  • 你需要[{ "Net income": 36886, "Net income before etc": 0, ... }, { "Net income": 18822593, ... }, ...]之类的东西吗?如果没有,您能否编辑您的问题并添加您想要的结果示例?
  • 抱歉,我没有看到您的编辑。我发布了一个答案,但我不确定它是否是你想要的。您需要向结果对象添加更多数据吗?

标签: javascript arrays json node.js npm


【解决方案1】:

好的,对于这个好问题,我有一个可靠的答案。但在我进入解决方案细节之前,这里是故事部分。

实际上,这个问题解决了现代 JS 编程中一个非常常见的问题,即动态获取和设置嵌套值。我开发了两种 Object 方法以功能性的方式满足这一需求。它们被称为Object.prototype.getNestedValue()Object.prototype.setNestedValue(),它们都用于动态获取和设置对象以及数组属性和值。

getNestedValue([prop1[, prop2[, prop3...]]]) 将返回嵌套属性的值。你可以像这样调用它

`myObj.getNestedValue(a,"prop2",1);`

其中myObj 是它被调用的对象,a 是一个动态变量(如果字符串类型表示对象属性,但如果它是数字类型则它是数组索引值),“prop2”是静态属性参数,最后一个 1 是数组的索引。所有提供的参数当然可以是动态的。如果你的参数在一个数组中,你可以像 getNestedValue(...[a,b,c,d]); 一样调用它

setNestedValue([prop1[, prop2[, prop3...]]],value) 就像它的双胞胎一样工作,但最后一个参数是要设置的值。如果该属性不存在,它将根据所提供参数的类型创建一个对象或数组。同样,字符串类型参数将产生一个对象,而数字类型参数将产生一个该大小的数组。

因此,一旦我们掌握了这些工具,您就会看到,这个问题所涉及的问题不再是问题。让我们看看代码。

Object.prototype.getNestedValue = function(...a) {
  return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};
Object.prototype.setNestedValue = function(...a) {
  a.length > 2 ? typeof this[a[0]] === "object" && this[a[0]] !== null ? this[a[0]].setNestedValue(...a.slice(1))
                                                                       : (this[a[0]] = typeof a[1] === "string" ? {} : new Array(a[1]),
                                                                         this[a[0]].setNestedValue(...a.slice(1)))
               : this[a[0]] = a[1];
  return this;
};

var   dataStr = '[{"id1":1001,"id2":1002,"id3":1004,"id4":1007,"id5":1013,"id6":1030,"id7":null,"id8":null,"id9":null,"accountid":1108,"name1":"Net Income","name2":"Net Income Before Extraordinary Item","name3":"Income (Loss) from operations                     ","name4":"Net Patient Revenue","name5":"Gross Patient Revenue","name6":"Inpatient Routine Services","name7":null,"name8":null,"name9":null,"accountname":"3000000 - INPATIENT REVENUE                       ","amt1":6266235,"amt2":0,"amt3":7085312,"amt4":7010901,"amt5":7008743,"amt6":6865373,"amt7":7298176,"amt8":7481711,"nCurMnth":0,"samt4":7010901,"id":1030,"parentid":1013,"relativelevel":6,"accountdetailid":null,"variancecalc":1,"InfoSetID":1108,"InfoSetName":"3000000 - INPATIENT REVENUE                       ","DetailType":"","DecimalCount":2},{"id1":1001,"id2":1002,"id3":1004,"id4":1007,"id5":1013,"id6":1030,"id7":null,"id8":null,"id9":null,"accountid":2708,"name1":"Net Income","name2":"Net Income Before Extraordinary Item","name3":"Income (Loss) from operations                     ","name4":"Net Patient Revenue","name5":"Gross Patient Revenue","name6":"Inpatient Routine Services","name7":null,"name8":null,"name9":null,"accountname":"3000090 - INPATIENT SWING BED                     ","amt1":190887,"amt2":0,"amt3":256581,"amt4":271789,"amt5":235998,"amt6":251224,"amt7":307154,"amt8":314971,"nCurMnth":0,"samt4":271789,"id":1030,"parentid":1013,"relativelevel":6,"accountdetailid":null,"variancecalc":1,"InfoSetID":2708,"InfoSetName":"3000090 - INPATIENT SWING BED                     ","DetailType":"","DecimalCount":2},{"id1":1001,"id2":1002,"id3":1004,"id4":1007,"id5":1013,"id6":1030,"id7":null,"id8":null,"id9":null,"accountid":1114,"name1":"Net Income","name2":"Net Income Before Extraordinary Item","name3":"Income (Loss) from operations                     ","name4":"Net Patient Revenue","name5":"Gross Patient Revenue","name6":"Inpatient Routine Services","name7":null,"name8":null,"name9":null,"accountname":"3999000 - SUSPENSE DAILY REV CLEAR ACCT           ","amt1":0,"amt2":0,"amt3":0,"amt4":0,"amt5":0,"amt6":0,"amt7":0,"amt8":0,"nCurMnth":0,"samt4":0,"id":1030,"parentid":1013,"relativelevel":6,"accountdetailid":null,"variancecalc":1,"InfoSetID":1114,"InfoSetName":"3999000 - SUSPENSE DAILY REV CLEAR ACCT           ","DetailType":"","DecimalCount":2},{"id1":1001,"id2":1002,"id3":1004,"id4":1007,"id5":1013,"id6":1031,"id7":null,"id8":null,"id9":null,"accountid":1133,"name1":"Net Income","name2":"Net Income Before Extraordinary Item","name3":"Income (Loss) from operations                     ","name4":"Net Patient Revenue","name5":"Gross Patient Revenue","name6":"Inpatient Ancillary Services","name7":null,"name8":null,"name9":null,"accountname":"3070000 - INTERP - INP                            ","amt1":36886,"amt2":0,"amt3":47968,"amt4":45109,"amt5":38047,"amt6":39158,"amt7":31290,"amt8":45148,"nCurMnth":0,"samt4":45109,"id":1031,"parentid":1013,"relativelevel":6,"accountdetailid":null,"variancecalc":1,"InfoSetID":1133,"InfoSetName":"3070000 - INTERP - INP                            ","DetailType":"","DecimalCount":2},{"id1":1001,"id2":1002,"id3":1004,"id4":1007,"id5":1013,"id6":1031,"id7":null,"id8":null,"id9":null,"accountid":1135,"name1":"Net Income","name2":"Net Income Before Extraordinary Item","name3":"Income (Loss) from operations                     ","name4":"Net Patient Revenue","name5":"Gross Patient Revenue","name6":"Inpatient Ancillary Services","name7":null,"name8":null,"name9":null,"accountname":"3100000 - INPATIENT ANCILLARY REV                 ","amt1":18822593,"amt2":0,"amt3":21676463,"amt4":21368866,"amt5":20284449,"amt6":21344632,"amt7":20272660,"amt8":21169123,"nCurMnth":0,"samt4":21368866,"id":1031,"parentid":1013,"relativelevel":6,"accountdetailid":null,"variancecalc":1,"InfoSetID":1135,"InfoSetName":"3100000 - INPATIENT ANCILLARY REV                 ","DetailType":"","DecimalCount":2},{"id1":1001,"id2":1002,"id3":1004,"id4":1007,"id5":1013,"id6":1031,"id7":null,"id8":null,"id9":null,"accountid":1273,"name1":"Net Income","name2":"Net Income Before Extraordinary Item","name3":"Income (Loss) from operations                     ","name4":"Net Patient Revenue","name5":"Gross Patient Revenue","name6":"Inpatient Ancillary Services","name7":null,"name8":null,"name9":null,"accountname":"3100090 - SWING BED INPATIENT REV                 ","amt1":166993,"amt2":0,"amt3":225651,"amt4":228349,"amt5":143726,"amt6":227736,"amt7":235705,"amt8":206381,"nCurMnth":0,"samt4":228349,"id":1031,"parentid":1013,"relativelevel":6,"accountdetailid":null,"variancecalc":1,"InfoSetID":1273,"InfoSetName":"3100090 - SWING BED INPATIENT REV                 ","DetailType":"","DecimalCount":2}]',
         data = JSON.parse(dataStr);

getKeysToNest = (o,...k) => Object.keys(o).reduce((p,c) => {var i = k.indexOf(c.slice(0, -1));
                                                            !!~i && (p[i] = !!p[i] ? p[i].concat(c) : [c]);
                                                            return p},[]),
        props = getKeysToNest(data[0],"id","name","amt"), // now we have the props to nest
       nested = data.map(d => props
                    .reduce((n,p) => p
                    .reduce((o,k,i) => o.setNestedValue(...(new Array(i)).fill("child"),k,d[k]),n) ,{}));
console.log(JSON.stringify(nested,null,2));

我们实际上在这里使用getKeysToNest() 函数来收集对我们很重要的键并将它们存储在一个数组中。然后我们将使用这个 props 数组来动态设置 setNestedValue() 方法的参数。这是一个非常简单的工作流程;

  • 我们将项目中的每个对象映射到它的嵌套形式。这是data.map(d => props 行。
  • props 数组包含 3 个数组,每个数组对应 id、name 和 amt。我们在.reduce((n,p) => p 一行中一一列举。
  • 在最后一行中,我们为每个键动态设置属性(为 id、name 和 amt 运行一次,每个键的每个级别运行一次)

我只是不知道将剩余的属性放在哪里。只需再次使用setNestedValue() 将它们过滤掉并在适当的级别插入它们即可。

如果有任何问题,我很乐意回答。

【讨论】:

    【解决方案2】:

    我不知道是否有这个包,另一方面,它似乎可以通过旅行名称轻松解决(直接因为它们与 id 匹配)。这将像relativelevel 所说的那样缩进条目,将它们放在每个对象的属性entry 中。

    // Configuration
    var max_attrs = 9; // Max attrs (ex: name1 .. name9).
    var ename = 'entry'; // Name of the entry.
    
    var output = {};
    
    function process_item(item) {
        // Dig into output
        var dig_n = item.relativelevel;
        var dig = output;
        while (--dig_n) {
            if (!(ename in dig))
                dig[ename] = {};
            dig = dig[ename];
        }
    
        // Generate entry
        var i = max_attrs;
        var entry;
    
        while (--i) {
            var name = item["name" + i];
            if (name) {
                if (!entry)
                    entry = name;
                else {
                    var entry_add = {};
                    entry_add[name] = entry;
                    entry = entry_add;
                }
            }
        };
    
        dig[ename] = entry  
    }
    
    data.forEach(function(item) {
        process_item(item);
    })
    
    // tree will be in variable output
    console.log(JSON.stringify(output))
    

    【讨论】:

    • 只有数组的 id1 和 name1 相同,其他的则不然。
    • 其他的应该放在哪里?例如amt3relativelevel
    • 哦,是的,relativelevel 显示了该节点在树中的级别。其他键暂时不相关。
    • relativelevel 始终为 6,因此树为 { { { { { { /* currently generated tree */ } } } } } }。这就是你想要的吗?
    • 不,它并不总是6,它只是原始数据的一部分。但是为了您的信息,在我的情况下,树的最大级别可以是 9。
    猜你喜欢
    • 2019-10-12
    • 1970-01-01
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    • 2017-07-19
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    相关资源
    最近更新 更多