简体   繁体   English

将按ASCII顺序排序的1D数组转换为Javascript中的嵌套数组

[英]Convert 1D array sorted by ASCII order to a nested array in Javascript

Assume this below array of objects that sorted by code property in ascii order: 假设下面按照ascii顺序按code属性排序的对象数组:

var codes = [
    { code: '01' },
    { code: '0101' },
    { code: '0102' },
    { code: '010201' },
    { code: '0103' },
    { code: '02' },
    { code: '0201' },
    { code: '0202' },
];

How can I convert this to a nested array like this : 如何将其转换为嵌套数组,如下所示:

var nestedCodes = [
    {
        code: '01',
        children: [
            { code: '0101' },
            {
                code: '0102',
                children: [
                    { code: '010201' }
                ]
            },
            { code: '0103' }
        ]
    },
    {
        code: '02',
        children: [
            { code: '0201' },
            { code: '0202' }
        ]
    }
];

The structure of codes is like concatenating multiple 0N that N can be a number between 1 and 9. Note that codes come from server and there would be some additional properties beside code like title but it doesn't matter in this problem. 代码的结构就像连接多个0NN可以是介于1和9之间的数字。请注意,代码来自服务器,除了title code之外还有一些其他属性,但在这个问题中无关紧要。

The main idea here is to make an appropriate format for jsTree . 这里的主要思想是为jsTree制作一个合适的格式。

You can do this with a recursive solution. 您可以使用递归解决方案执行此操作。 The idea is to maintain the path (obtained as an array via String.prototype.match with a regex) and the parent under which you want to insert the code for each recursive call. 我们的想法是维护path (通过带有正则表达式的String.prototype.match作为数组获得)和您希望在每个递归调用下插入codeparent

The parent keeps track of the node you want to pick in the "current" recursive call, and path helps in building the parent as you keep going deeper: parent跟踪您要在“当前”递归调用中选择的节点,并且path有助于在您继续深入时构建parent

 function insert(d, path, parent, arr) { if (path.length === 0) { arr.push(Object.assign({}, d)); return; } var target = arr.find(e => e.code === parent); target.children = target.children || []; insert(d, path.slice(1), parent + path[0], target.children); } var codes = [ { code: '01' }, { code: '0101' }, { code: '0102' }, { code: '010201' }, { code: '0103' }, { code: '02' }, { code: '0201' }, { code: '0202' }, ]; var res = codes.reduce((a, c) => { var p = c.code.match(/(0[1-9])/g); insert(c, p.slice(1), p[0], a); return a; }, []); console.log(res); 

The assumption, of course, is that when a code is being inserted, its parent has already been inserted before. 当然,假设是在插入code时,之前已经插入了其父code

I struggled quite a bit to write the recursive function that will build the required structure. 我努力编写将构建所需结构的递归函数。 Found the answer here 这里找到答案

But to do that, you must first add parent property to each of your codes array. 但要做到这一点,您必须首先将parent属性添加到每个codes数组。 I did that on the assumption that each code has a parent that is equivalent to the code itself except for the last two bytes. 我这样做的假设是每个code都有一个父代,它除了最后两个字节之外等同于代码本身。

 var codes = [{code: '01' }, {code: '0101' }, {code: '0102' }, {code: '010201'}, {code: '0103' }, {code: '02' }, {code: '0201' }, {code: '0202' }, ]; // add parents to each code codes.forEach(function(c) { if (c.code.length > 2) { c.parent = c.code.substr(0, c.code.length - 2); } else { c.parent = 'root'; } }); function find_children(arr, parent) { var out = []; for (var i in arr) { if (arr[i].parent == parent) { var children = find_children(arr, arr[i].code); if (children.length) { arr[i].children = children; } out.push(arr[i]) } } return out; } var nested = find_children(codes,'root'); console.log(nested); 

The code is a little long but pretty easy to understand in my opinion. 代码有点长,但在我看来很容易理解。 It's extremely robust - does not require the array to be sorted and doesn't require 01 to exist to process 0102 (in case it's needed). 它非常强大 - 不需要对数组进行排序,并且不需要01来处理0102 (如果需要)。 The code can be much shorter without handling these cases, but I thought you might be need this. 如果不处理这些情况,代码可以更短,但我认为你可能需要这个。

Firstly, create a object-based tree data structure out of the data. 首先,从数据中创建基于对象的树数据结构。 This tree has keys and values, and is very efficient to build because accessing by index is O(1). 此树具有键和值,并且构建非常有效,因为通过索引访问是O(1)。 Next, convert the object-based tree into the final array-based tree data structure by traversing the object-based tree and then converting each layer into arrays. 接下来,通过遍历基于对象的树,然后将每个图层转换为数组,将基于对象的树转换为最终的基于数组的树数据结构。

I also make heavy use of recursion since recursion is well suited for creating and traversing trees. 我也大量使用递归,因为递归非常适合创建和遍历树。

Compared to the other answers, my algorithm has better time complexity because I create a dictionary/object which has O(1) access when creating the tree. 与其他答案相比,我的算法具有更好的时间复杂度,因为我创建了一个在创建树时具有O(1)访问权限的字典/对象。 The other algorithms do a search within each layer, which is inefficient. 其他算法在每个层内进行搜索,这是低效的。 My algorithm runs in O(N) whereas the other answers here are shorter but run in O(N^2). 我的算法在O(N)中运行,而此处的其他答案更短但在O(N ^ 2)中运行。

Just copy the format function into your code and it should be good to use. 只需将format功能复制到您的代码中即可使用。

 const codes = [ { code: '01' }, { code: '0101' }, { code: '0102' }, { code: '010201' }, { code: '0103' }, { code: '02' }, { code: '0201' }, { code: '0202' }, ]; function format(codes) { // Splits the string into an array of 2-character strings. const SPLIT_REGEX = /.{2}(?=(.{2})+(?!.))|.{2}$/g; const codeFragments = codes.map(obj => obj.code.match(SPLIT_REGEX)); // 1. Represent the data as a tree which is more efficient to build. const tree = {}; function createTree(tree, fragments) { let node = tree; fragments.forEach(fragment => { if (!node[fragment]) { node[fragment] = {}; } node = node[fragment]; }); } codeFragments.forEach(fragments => createTree(tree, fragments)); /* tree will have the structure: { "01": { "01": {}, "02": { "01": {} }, "03": {} }, "02": { "01": {}, "02": {} } } */ // 2. Convert the tree structure into the desired format. function generateCodesFromTree(tree, previous) { const nestedCodes = []; Object.keys(tree).forEach(treeNode => { const code = previous + treeNode; const children = generateCodesFromTree(tree[treeNode], code); const nestedCode = { code }; if (children.length > 0) { nestedCode.children = children; } nestedCodes.push(nestedCode); }); return nestedCodes; } return generateCodesFromTree(tree, ''); } console.log(format(codes)); 

It can only be achieved by using a recursive approach. 它只能通过使用递归方法来实现。 Try this one. 试试这个吧。

 let codes = [ { code: '01' }, { code: '0101' }, { code: '0102' }, { code: '010201' }, { code: '0103' }, { code: '02' }, { code: '0201' }, { code: '0202' }, ]; roots = codes.filter(c => c.code.length === 2); roots.forEach(c => assign(c)); console.log(roots); function assign(code) { codes.forEach(c => { if (c !== code) { if (code.code === c.code.slice(0, code.code.length)) { code.children = !code.children ? [c] : [...code.children, c]; assign(code.children[code.children.length - 1]); } } }); } 

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

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