简体   繁体   English

展平深度嵌套的对象数组

[英]Flattening deeply nested array of objects

I got following data structure, which is an array of accounts objects, where some accounts are being parents to its children accounts, which in turn can be parents to other accounts etc.:我得到了以下数据结构,它是一个帐户对象数组,其中一些帐户是其子帐户的父级,而子级帐户又可以是其他帐户的父级等:

[{
  "id": "acc.1260446672222.11",
  "type": "EXPENSES_FOLDER",
  "name": "Expense Group",
  "balance": 3418.11,
  "children": [{
    "id": "acc.1260446672238.27",
    "type": "EXPENSE",
    "name": "Advertising, Promotion and Entertainment Account",
    "balance": 0,
    "children": []
  }, {
    "id": "acc.9a2492ba-0d82-4f4a-a1b4-14868f1e1a39",
    "type": "EXPENSES_FOLDER",
    "name": "Premises Costs",
    "balance": 0,
    "children": [{
      "id": "acc.287ba5b6-5536-428b-950f-d71d2af73ccc",
      "type": "EXPENSE",
      "name": "Use of Home - Gas",
      "balance": 0,
      "children": [

      ]
    }, {
      "id": "acc.7091ee15-3f02-4bd1-94e5-5918cf986969",
      "type": "EXPENSE",
      "name": "Hire of Venue, Studios, Teaching Rooms",
      "balance": 0,
      "children": [

      ]
    }]
  }, {
    "id": "acc.827ec446-edeb-4f2b-8032-d306292d2d83",
    "type": "EXPENSES_FOLDER",
    "name": "Administrative Expenses",
    "balance": 558.61,
    "children": [{
      "id": "acc.0ed5fc81-7734-4452-86a9-db22a6b0f8e8",
      "type": "EXPENSE",
      "name": "Bank Charges",
      "balance": 15,
      "children": [

      ]
    }, {
      "id": "acc.e2cdb2c0-8565-4991-a35a-d4596b0ddf45",
      "type": "EXPENSE",
      "name": "Software & Computer Peripherals",
      "balance": 417.13,
      "children": [

      ]
    }, {
      "id": "acc.96d5d00e-43f4-4d3a-b97b-fdf258c65514",
      "type": "EXPENSE",
      "name": "Printing, photocopying etc",
      "balance": 55.93,
      "children": [

      ]
    }, {
      "id": "acc.494dd64a-4fb3-42b8-be3e-8f3b59a2ef59",
      "type": "EXPENSE",
      "name": "Artists Administration Service",
      "balance": 0,
      "children": [

      ]
    }, {
      "id": "acc.1260446672238.35",
      "type": "EXPENSE",
      "name": "Stationery",
      "balance": 0,
      "children": [

      ]
    }, {
      "id": "acc.96d89d0d-5465-488b-b37f-d41ca114c5e6",
      "type": "EXPENSE",
      "name": "Mobile Telephone",
      "balance": 41.19,
      "children": [

      ]
    }, {
      "id": "acc.1260446672238.33",
      "type": "EXPENSE",
      "name": "Home Telephone",
      "balance": 0,
      "children": [

      ]
    }, {
      "id": "acc.1260446672238.38",
      "type": "EXPENSE",
      "name": "Postage/delivery",
      "balance": 29.36,
      "children": [

      ]
    }]
  }, {
    "id": "acc.b9c9bbc7-43df-472e-9ac8-c7c76f08f49a",
    "type": "EXPENSES_FOLDER",
    "name": "Instruments, Equipment Maintenance etc",
    "balance": 1002.48,
    "children": [{
      "id": "acc.1260446672238.32",
      "type": "OTHER_EXPENSES",
      "name": "Instrument Insurance",
      "balance": 157.48,
      "children": [

      ]
    }, {
      "id": "acc.2a1cca15-2868-4770-a3e7-d43a6268c6a1",
      "type": "EXPENSE",
      "name": "Instrument Repairs & Maintenance",
      "balance": 845,
      "children": [

      ]
    }, {
      "id": "acc.a908aee0-84fb-450a-916b-4cec25265aef",
      "type": "EXPENSE",
      "name": "Accessories & Replacement Parts",
      "balance": 0,
      "children": [

      ]
    }]
  }, {
    "id": "acc.a42cdd86-0d9e-4f3f-af0d-7c4525374731",
    "type": "EXPENSES_FOLDER",
    "name": "Motor Vehicle",
    "balance": 0,
    "children": [{
      "id": "acc.cb325e7e-0ce4-4c78-9cb4-20659df733a6",
      "type": "EXPENSE",
      "name": "Fuel and Oil",
      "balance": 0,
      "children": [

      ]
    }]
  }, {
    "id": "acc.4bdd9e26-ce64-4e7f-b46a-82ec9de06ded",
    "type": "EXPENSES_FOLDER",
    "name": "Other Travel",
    "balance": 132.1,
    "children": [{
      "id": "acc.77dd2142-f2de-4a2c-9247-061d0661bc0a",
      "type": "EXPENSE",
      "name": "Taxis",
      "balance": 24.5,
      "children": [

      ]
    }, {
      "id": "acc.2b54abdd-7ef5-43cd-bdb9-c8c981b59ff2",
      "type": "EXPENSE",
      "name": "Public Transport",
      "balance": 107.6,
      "children": [

      ]
    }]
  }, {
    "id": "acc.e4695b70-31fa-4e23-afd0-97335dcd5b9e",
    "type": "EXPENSE",
    "name": "Subsitence",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.02d222bf-4dff-4308-afe9-69b93f412ada",
    "type": "EXPENSE",
    "name": "Hotel and Accomodation",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.d61cd5b4-2c80-4ab8-93d0-9d5726bd253b",
    "type": "EXPENSES_FOLDER",
    "name": "Fees and Commission Paid",
    "balance": 0,
    "children": [{
      "id": "acc.1262189019758.7",
      "type": "EXPENSE",
      "name": "Pupils exam entry fees",
      "balance": 0,
      "children": [

      ]
    }, {
      "id": "acc.a7d7efd3-d0da-4704-babb-079b6077f3fe",
      "type": "EXPENSE",
      "name": "Audition, competition entry fees",
      "balance": 0,
      "children": [

      ]
    }, {
      "id": "acc.3b91ee4e-40a8-46d8-aa05-3afa5974b3ef",
      "type": "EXPENSE",
      "name": "Deputies, Other Musicians",
      "balance": 0,
      "children": [

      ]
    }]
  }, {
    "id": "acc.250d6872-6023-4599-a0b6-b7159eebbfa1",
    "type": "EXPENSES_FOLDER",
    "name": "Other Professional Expenses",
    "balance": 1739.42,
    "children": [{
      "id": "acc.b7315228-f85a-4ffb-9199-d1128a409e5f",
      "type": "EXPENSE",
      "name": "Promotion & Publicity",
      "balance": 138.6,
      "children": [

      ]
    }, {
      "id": "acc.69ca2005-d7a0-448b-b70c-dafb128a48ae",
      "type": "EXPENSE",
      "name": "Other Expenses",
      "balance": 364.5,
      "children": [

      ]
    }, {
      "id": "acc.dcd999d2-4e18-41be-b9cc-218d4034b88e",
      "type": "EXPENSE",
      "name": "Office Equipment, Furniture",
      "balance": 0,
      "children": [

      ]
    }, {
      "id": "acc.e0460706-d5c9-4c40-9d1e-0d2058864b92",
      "type": "EXPENSE",
      "name": "CDs, Dowloads etc",
      "balance": 67.57,
      "children": [

      ]
    }, {
      "id": "acc.1866df79-9e44-459a-a978-727904987469",
      "type": "EXPENSE",
      "name": "Professional Books, Magazines",
      "balance": 104.01,
      "children": [

      ]
    }, {
      "id": "acc.24c1651d-e7ae-48bc-a32d-311427e0fcea",
      "type": "EXPENSE",
      "name": "Professional Associations",
      "balance": 272.17,
      "children": [

      ]
    }, {
      "id": "acc.289ab0ac-b9d3-435e-ac82-9da9702b7d4b",
      "type": "EXPENSE",
      "name": "Tuition",
      "balance": 470,
      "children": [

      ]
    }, {
      "id": "acc.f24cf99b-6291-4b9f-821e-425f4909d4e1",
      "type": "EXPENSE",
      "name": "Scores, Manuscript Paper etc",
      "balance": 215.32,
      "children": [

      ]
    }, {
      "id": "acc.1af95953-56f0-455e-9d0a-7c4e0477cf0d",
      "type": "EXPENSE",
      "name": "Performance Clothing",
      "balance": 0,
      "children": [

      ]
    }, {
      "id": "acc.c0585577-535a-4ae2-a02b-e5b249f67c67",
      "type": "EXPENSE",
      "name": "Concerts, Shows etc",
      "balance": 107.25,
      "children": [

      ]
    }]
  }, {
    "id": "acc.1260446672222.24",
    "type": "ADMIN",
    "name": "Administrative Expenses",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.1260446672238.26",
    "type": "TRAVEL",
    "name": "Travel and Subsistence Account",
    "balance": -14.5,
    "children": [

    ]
  }, {
    "id": "acc.1260446672238.28",
    "type": "LEGAL",
    "name": "Legal and Professional Costs Account",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.1260446672238.36",
    "type": "OTHER_EXPENSES",
    "name": "Rent/Rates",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.1262191376548.37",
    "type": "EXPENSE",
    "name": "Research",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.1262191388329.38",
    "type": "EXPENSE",
    "name": "Professional Development",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.1262192291558.52",
    "type": "EXPENSE",
    "name": "Professional Presentation",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.1262193596634.72",
    "type": "EXPENSE",
    "name": "Subscriptions",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.1262265941130.16",
    "type": "EXPENSE",
    "name": "Piano accompaniment",
    "balance": 0,
    "children": [

    ]
  }, {
    "id": "acc.1267370824329.1",
    "type": "EXPENSE",
    "name": "Cost of Sales",
    "balance": 0,
    "children": [

    ]
  }]
}]

What I need is to flatten this array to have a flat list of accounts.我需要的是展平这个数组以获得一个平面的账户列表。 What's the way to proceed with that in Vanilla JavaScript .Vanilla JavaScript中进行该操作的方法是什么。 (I also got access to lodash methods in my project). (我也可以在我的项目中访问lodash方法)。

Just iterate over and if a children is found get the array from the children concatinated.只需迭代,如果找到孩子,则从连接的孩子中获取数组。

 function flat(array) { var result = []; array.forEach(function (a) { result.push(a); if (Array.isArray(a.children)) { result = result.concat(flat(a.children)); } }); return result; } var data = [{ id: "acc.1260446672222.11", type: "EXPENSES_FOLDER", name: "Expense Group", balance: 3418.11, children: [{ id: "acc.1260446672238.27", type: "EXPENSE", name: "Advertising, Promotion and Entertainment Account", balance: 0, children: [] }, { id: "acc.9a2492ba-0d82-4f4a-a1b4-14868f1e1a39", type: "EXPENSES_FOLDER", name: "Premises Costs", balance: 0, children: [{ id: "acc.287ba5b6-5536-428b-950f-d71d2af73ccc", type: "EXPENSE", name: "Use of Home - Gas", balance: 0, children: [] }, { id: "acc.7091ee15-3f02-4bd1-94e5-5918cf986969", type: "EXPENSE", name: "Hire of Venue, Studios, Teaching Rooms", balance: 0, children: [] }] }, { id: "acc.827ec446-edeb-4f2b-8032-d306292d2d83", type: "EXPENSES_FOLDER", name: "Administrative Expenses", balance: 558.61, children: [{ id: "acc.0ed5fc81-7734-4452-86a9-db22a6b0f8e8", type: "EXPENSE", name: "Bank Charges", balance: 15, children: [] }, { id: "acc.e2cdb2c0-8565-4991-a35a-d4596b0ddf45", type: "EXPENSE", name: "Software & Computer Peripherals", balance: 417.13, children: [] }, { id: "acc.96d5d00e-43f4-4d3a-b97b-fdf258c65514", type: "EXPENSE", name: "Printing, photocopying etc", balance: 55.93, children: [] }, { id: "acc.494dd64a-4fb3-42b8-be3e-8f3b59a2ef59", type: "EXPENSE", name: "Artists Administration Service", balance: 0, children: [] }, { id: "acc.1260446672238.35", type: "EXPENSE", name: "Stationery", balance: 0, children: [] }, { id: "acc.96d89d0d-5465-488b-b37f-d41ca114c5e6", type: "EXPENSE", name: "Mobile Telephone", balance: 41.19, children: [] }, { id: "acc.1260446672238.33", type: "EXPENSE", name: "Home Telephone", balance: 0, children: [] }, { id: "acc.1260446672238.38", type: "EXPENSE", name: "Postage/delivery", balance: 29.36, children: [] }] }, { id: "acc.b9c9bbc7-43df-472e-9ac8-c7c76f08f49a", type: "EXPENSES_FOLDER", name: "Instruments, Equipment Maintenance etc", balance: 1002.48, children: [{ id: "acc.1260446672238.32", type: "OTHER_EXPENSES", name: "Instrument Insurance", balance: 157.48, children: [] }, { id: "acc.2a1cca15-2868-4770-a3e7-d43a6268c6a1", type: "EXPENSE", name: "Instrument Repairs & Maintenance", balance: 845, children: [] }, { id: "acc.a908aee0-84fb-450a-916b-4cec25265aef", type: "EXPENSE", name: "Accessories & Replacement Parts", balance: 0, children: [] }] }, { id: "acc.a42cdd86-0d9e-4f3f-af0d-7c4525374731", type: "EXPENSES_FOLDER", name: "Motor Vehicle", balance: 0, children: [{ id: "acc.cb325e7e-0ce4-4c78-9cb4-20659df733a6", type: "EXPENSE", name: "Fuel and Oil", balance: 0, children: [] }] }, { id: "acc.4bdd9e26-ce64-4e7f-b46a-82ec9de06ded", type: "EXPENSES_FOLDER", name: "Other Travel", balance: 132.1, children: [{ id: "acc.77dd2142-f2de-4a2c-9247-061d0661bc0a", type: "EXPENSE", name: "Taxis", balance: 24.5, children: [] }, { id: "acc.2b54abdd-7ef5-43cd-bdb9-c8c981b59ff2", type: "EXPENSE", name: "Public Transport", balance: 107.6, children: [] }] }, { id: "acc.e4695b70-31fa-4e23-afd0-97335dcd5b9e", type: "EXPENSE", name: "Subsitence", balance: 0, children: [] }, { id: "acc.02d222bf-4dff-4308-afe9-69b93f412ada", type: "EXPENSE", name: "Hotel and Accomodation", balance: 0, children: [] }, { id: "acc.d61cd5b4-2c80-4ab8-93d0-9d5726bd253b", type: "EXPENSES_FOLDER", name: "Fees and Commission Paid", balance: 0, children: [{ id: "acc.1262189019758.7", type: "EXPENSE", name: "Pupils exam entry fees", balance: 0, children: [] }, { id: "acc.a7d7efd3-d0da-4704-babb-079b6077f3fe", type: "EXPENSE", name: "Audition, competition entry fees", balance: 0, children: [] }, { id: "acc.3b91ee4e-40a8-46d8-aa05-3afa5974b3ef", type: "EXPENSE", name: "Deputies, Other Musicians", balance: 0, children: [] }] }, { id: "acc.250d6872-6023-4599-a0b6-b7159eebbfa1", type: "EXPENSES_FOLDER", name: "Other Professional Expenses", balance: 1739.42, children: [{ id: "acc.b7315228-f85a-4ffb-9199-d1128a409e5f", type: "EXPENSE", name: "Promotion & Publicity", balance: 138.6, children: [] }, { id: "acc.69ca2005-d7a0-448b-b70c-dafb128a48ae", type: "EXPENSE", name: "Other Expenses", balance: 364.5, children: [] }, { id: "acc.dcd999d2-4e18-41be-b9cc-218d4034b88e", type: "EXPENSE", name: "Office Equipment, Furniture", balance: 0, children: [] }, { id: "acc.e0460706-d5c9-4c40-9d1e-0d2058864b92", type: "EXPENSE", name: "CDs, Dowloads etc", balance: 67.57, children: [] }, { id: "acc.1866df79-9e44-459a-a978-727904987469", type: "EXPENSE", name: "Professional Books, Magazines", balance: 104.01, children: [] }, { id: "acc.24c1651d-e7ae-48bc-a32d-311427e0fcea", type: "EXPENSE", name: "Professional Associations", balance: 272.17, children: [] }, { id: "acc.289ab0ac-b9d3-435e-ac82-9da9702b7d4b", type: "EXPENSE", name: "Tuition", balance: 470, children: [] }, { id: "acc.f24cf99b-6291-4b9f-821e-425f4909d4e1", type: "EXPENSE", name: "Scores, Manuscript Paper etc", balance: 215.32, children: [] }, { id: "acc.1af95953-56f0-455e-9d0a-7c4e0477cf0d", type: "EXPENSE", name: "Performance Clothing", balance: 0, children: [] }, { id: "acc.c0585577-535a-4ae2-a02b-e5b249f67c67", type: "EXPENSE", name: "Concerts, Shows etc", balance: 107.25, children: [] }] }, { id: "acc.1260446672222.24", type: "ADMIN", name: "Administrative Expenses", balance: 0, children: [] }, { id: "acc.1260446672238.26", type: "TRAVEL", name: "Travel and Subsistence Account", balance: -14.5, children: [] }, { id: "acc.1260446672238.28", type: "LEGAL", name: "Legal and Professional Costs Account", balance: 0, children: [] }, { id: "acc.1260446672238.36", type: "OTHER_EXPENSES", name: "Rent/Rates", balance: 0, children: [] }, { id: "acc.1262191376548.37", type: "EXPENSE", name: "Research", balance: 0, children: [] }, { id: "acc.1262191388329.38", type: "EXPENSE", name: "Professional Development", balance: 0, children: [] }, { id: "acc.1262192291558.52", type: "EXPENSE", name: "Professional Presentation", balance: 0, children: [] }, { id: "acc.1262193596634.72", type: "EXPENSE", name: "Subscriptions", balance: 0, children: [] }, { id: "acc.1262265941130.16", type: "EXPENSE", name: "Piano accompaniment", balance: 0, children: [] }, { id: "acc.1267370824329.1", type: "EXPENSE", name: "Cost of Sales", balance: 0, children: [] }] }], result = flat(data); document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

try to understand this version, with it's benefits and implications尝试理解这个版本,它的好处和含义

function flatten(into, node){
    if(node == null) return into;
    if(Array.isArray(node)) return node.reduce(flatten, into);
    into.push(node);
    return flatten(into, node.children);
}

var out = flatten([], yourArray);

the argument order might be confusing at first, unless you are used to reduced, or the concept of options first, data last , wich is very handy when it comes to FP and currying or partial application.参数顺序一开始可能会令人困惑,除非您习惯于减少,或者选项的概念首先,数据最后,这在 FP 和柯里化或部分应用程序方面非常方便。

You could make a recursive function that looks through each account, and if it has children calls itself on the children, the function should return a flattened array of accounts.您可以创建一个遍历每个帐户的递归函数,如果它有子级调用自己的子级,该函数应该返回一个扁平的帐户数组。

This example is a bit more complicated than it has to be because I am assuming that order matters and that the parent should be before the children.这个例子比它必须的要复杂一些,因为我假设顺序很重要,并且父母应该孩子之前。

 function flattenAccounts(accounts){ var a = []; for(var i=0;i<accounts.length;i++){ var o = accounts[i]; if(o.children){ var c = flattenAccounts(o.children); if(c){ a = a.concat(c); } } a.push(o) } return a; }

This is a simpler example but the children would end up before the parent.这是一个更简单的例子,但孩子最终会在父母之前结束。

 function flattenAccounts(accounts){ var a = []; for(var i=0;i<accounts.length;i++){ if(accounts[i].children){ a = a.concat(flattenAccounts(accounts[i].children) ) } a.push(accounts[i]) } return a; }

You can loop through the array entries, check if it's an array with isArray() and recursively flatten the entries of the array as below:您可以遍历数组条目,使用isArray()检查它是否是一个数组,并递归地展平数组的条目,如下所示:

function deepFlattenArray(arr) {
  const finalArray = [];
  // Loop through the array contents
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      // Recursively flatten entries that are arrays and push into the finalArray
      finalArray.push(...deepFlattenArray(arr[i]));
    } else {
      // Push entries that are not arrays
      finalArray.push(arr[i]);
    }
  }
  return finalArray;
}

You can also use javascript recursive map function and get the children nodes of the nested array, like this:您还可以使用 javascript 递归映射函数并获取嵌套数组的子节点,如下所示:

const dataTree = [//array above];
const getMembers = (members) => {
  let children = [];
  const flattenMembers = members.map(m => {
    if (m.children && m.children.length) {
      children = [...children, ...m.children];
    }
    return m;
  });

  return flattenMembers.concat(children.length ? getMembers(children) : children);
};

getMembers(dataTree);

Source Here 来源这里

I'd like to point out that all answers work by copying the children to the root, but leaving the original children in place, and hence creating duplicates - not a flat array.我想指出,所有答案都通过将孩子复制到根目录来工作,但将原始孩子留在原处,从而创建重复项 - 而不是平面数组。

Depending on how the data is used, this might lead to issues.根据数据的使用方式,这可能会导致问题。 I would certainly not want to be caught by my team leaving a messy array behind.我当然不想被我的团队抓住,留下一个凌乱的阵列。

I couldn't find a solution to remove the children within the recursive function, so I added a second "cleanup" function.我找不到在递归函数中删除子项的解决方案,所以我添加了第二个“清理”函数。

Based on the accepted answer by Nina Scholz:根据 Nina Scholz 接受的答案:

function flattenArr(arr) {

  var dirtyArr = function(arr) {
    let result = [];

    arr.forEach((arrItem) => {
      result.push(arrItem);

      if (Array.isArray(arrItem.children)) {
        result = result.concat(dirtyArr(arrItem.children));
      }
    });

    return result;
  };

  var cleanArr = function(arr) {
    arr.forEach((arrItem) => {
      if (arrItem.hasOwnProperty('children')) {
        delete arrItem.children;
      }
    });
    return arr;
  };

  return cleanArr(dirtyArr(arr));

}

Yet another approach.. It's simple but it works.另一种方法.. 这很简单但很有效。

function flattenChildren(arr: any): any {
  return arr.flatMap(({ children, ...o }: { children: any; o: any }) => [
    o,
    ...flattenChildren(children),
  ])
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM