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

时间:2018-11-27 05:55:00

标签: javascript arrays jstree

假设下面的对象数组按code属性以ascii顺序排序:

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

如何将其转换为这样的嵌套数组:

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

代码的结构就像连接多个0N一样,N可以是1到9之间的数字。请注意,代码来自服务器,并且code旁还有一些其他属性像title一样,但是在这个问题中都没关系。

这里的主要思想是为jsTree设置适当的格式。

4 个答案:

答案 0 :(得分:1)

您可以使用递归解决方案。想法是维护path(通过带有正则表达式的String.prototype.match作为数组)和parent,在其中要为每个递归调用插入code

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);

当然,假设是在插入code时,其父级已经被插入。

答案 1 :(得分:1)

我花了很多时间来编写将构建所需结构的递归函数。找到了答案here

但是要这样做,必须首先向每个parent数组添加codes属性。 我这样做的前提是,每个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);

答案 2 :(得分:1)

代码有点长,但是在我看来很容易理解。它非常健壮-不需要对数组进行排序,也不需要存在01来处理0102(如果需要)。如果不处理这些情况,代码可能会短得多,但是我认为您可能需要此代码。

首先,从数据中创建一个基于对象的树数据结构。该树具有键和值,并且由于按索引访问为O(1),因此构建起来非常有效。接下来,遍历基于对象的树,然后将每一层转换为数组,从而将基于对象的树转换为最终的基于数组的树数据结构。

我还大量使用了递归,因为递归非常适合创建和遍历树。

与其他答案相比,我的算法具有更好的时间复杂度,因为我在创建树时创建了具有O(1)访问权限的字典/对象。其他算法在每个层中进行搜索,效率低下。我的算法以O(N)运行,而这里的其他答案较短,但以O(N ^ 2)运行。

只需将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));

答案 3 :(得分:0)

只能通过使用递归方法来实现。试试这个。

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]);
      }
    }
  });
}