如何根据字符串索引对对象数组进行分组和排序

How to group and sort object array based on string index

我想将我的平面数组分组并重新排列成一个对象数组,其中每个对象都在其子属性中包含其直接后代。

我的输入数据-:

const data = [
  { lineNo: '1' },
  { lineNo: '1-1' },
  { lineNo: '1-2' },
  { lineNo: '1-3' },
  { lineNo: '1-4' },
  { lineNo: '1-11' },
  { lineNo: '1-2-1' },
  { lineNo: '1-2-3' },
  { lineNo: '1-2-2' },
  { lineNo: '1-2-4' },
  { lineNo: '1-3-1' },
  { lineNo: '1-3-2' },
  { lineNo: '1-3-3' },
  { lineNo: '1-11-1' },
  { lineNo: '1-11-2' },
  { lineNo: '1-11-3' },
  { lineNo: '2' },
  { lineNo: '2-1' },
  { lineNo: '2-2' },
  { lineNo: '2-3' },
  { lineNo: '2-2-1' },
  { lineNo: '2-2-2' },
];

我想要的输出-:

const newData = [
  {
    lineNo: '1',
    children: [{ lineNo: '1-1' }, { lineNo: '1-2' }, { lineNo: '1-3' }, { lineNo: '1-4' }, { lineNo: '1-11' }],
  },
  {
    lineNo: '1-2',
    children: [{ lineNo: '1-2-1' }, { lineNo: '1-2-2' }, { lineNo: '1-2-3' }, { lineNo: '1-2-4' }],
  },
  {
    lineNo: '1-3',
    children: [{ lineNo: '1-3-1' }, { lineNo: '1-3-2' }, { lineNo: '1-3-3' }],
  },
  {
    lineNo: '1-11',
    children: [{ lineNo: '1-11-1' }, { lineNo: '1-11-2' }, { lineNo: '1-11-3' }],
  },
  {
    lineNo: '2',
    children: [{ lineNo: '2-1' }, { lineNo: '2-2' }, { lineNo: '2-3' }],
  },
  {
    lineNo: '2-2',
    children: [{ lineNo: '2-2-1' }, { lineNo: '2-2-2' }],
  },
];

注意:生成的数据必须遵循输出中提到的数字顺序。

到目前为止我做了什么-:

const data = [
  { lineNo: '1' },
  { lineNo: '1-1' },
  { lineNo: '1-2' },
  { lineNo: '1-3' },
  { lineNo: '1-4' },
  { lineNo: '1-11' },
  { lineNo: '1-11-1' },
  { lineNo: '1-11-2' },
  { lineNo: '1-11-3' },
  { lineNo: '1-2-1' },
  { lineNo: '1-2-3' },
  { lineNo: '1-2-2' },
  { lineNo: '1-2-4' },
  { lineNo: '1-2-1-1' },
  { lineNo: '1-2-1-2' },
  { lineNo: '1-2-1-3' },
  { lineNo: '1-3-1' },
  { lineNo: '1-3-2' },
  { lineNo: '1-3-3' },
];

function createTree(data: any) {
  const tree: any[] = [];
  data.reduce(
    (r: any, o: any) => {
      o.lineNo
        .split('-')
        .map((_: any, i: any, a: any) => a.slice(0, i + 1).join('-'))
        .reduce((q: any, lineNo: any, i: any, { length }: any) => {
          let temp = (q.children = q.children || []).find((p: any) => p.lineNo === lineNo);
          if (!temp) {
            q.children.push((temp = { lineNo }));
          }
          if (i + 1 === length) {
            Object.assign(temp, o);
          }
          return temp;
        }, r);
      return r;
    },
    { children: tree }
  );

  return tree;
}

const createFlat = (data: any) => {
  const flat: any[] = [];
  const flatData = (data: any) => {
    data.forEach((item: any) => {
      if (item.children) {
        flat.push(item);
        flatData(item.children);
      }
    });
  };
  flatData(data);
  return JSON.parse(JSON.stringify(flat));
};

const getCleanData = (data: any) => {
  const cleanData: any[] = [];
  function delChildren(obj: any) {
    if (obj.children) {
      obj.children.forEach((item: any) => {
        if (item.children) {
          delete item.children;
        }
      });
    }
    return obj;
  }

  data.forEach(function (item: any) {
    cleanData.push(delChildren(item));
  });

  return cleanData;
};

const treeData = createTree(data);
const flatData = createFlat(treeData);
const cleanData = getCleanData(flatData);
console.log(JSON.stringify(cleanData, null, 2));

我正在寻找一种简单明了的方法。

谢谢!

您可以先使用“自然”排序对数据进行排序。 JavaScript localeCompare 有一个选项。

然后创建一个由 lineNo 键控的 Map 并将具有相同 lineNo 属性 和空 children 数组的对象作为相应的值 属性.

然后再次迭代数据以填充那些 children 数组。

提取Map值并删除那些具有空children数组的对象,除非它们是top-level节点(边界例)。

这是一个实现:

function createTree(data) {
    // Apply natural ordering
    data = [...data].sort((a, b) => 
        a.lineNo.localeCompare(b.lineNo, "en", { numeric: true })
    );
    // Create key/value pairs for all lineNo in a Map
    let map = new Map(data.map(({lineNo}) => [lineNo, { lineNo, children: [] }]));
    // Populate the children arrays
    for (let {lineNo} of data) {
        map.get(lineNo.replace(/-?\d+$/, ""))?.children?.push({lineNo});
    }
    // Exclude the nodes that have no children, except if they are top-level
    return [...map.values()].filter(({lineNo, children}) => 
        !lineNo.includes("-") || children.length
    );
}

// Demo
const data = [{ lineNo: '1-1' },{ lineNo: '1-2' },{ lineNo: '1-3-2' },{ lineNo: '1-11-2' },{ lineNo: '1-3' },{ lineNo: '1-11' },{ lineNo: '1-2-3' },{ lineNo: '1-2-2' },{ lineNo: '1' },{ lineNo: '1-2-1' },{ lineNo: '1-2-4' },{ lineNo: '1-11-1' },{ lineNo: '1-3-1' },{ lineNo: '1-4' },{ lineNo: '1-3-3' },{ lineNo: '2-1' },{ lineNo: '2-2' },{ lineNo: '2-2-1' },{ lineNo: '1-11-3' },{ lineNo: '2' },{ lineNo: '2-3' },{ lineNo: '2-2-2' },];

console.log(createTree(data));