简体   繁体   English

使用 lodash 或 vanilla js 重新排序包含父/子关系的对象数组

[英]Reorder array of objects that contain parent/child relationships with lodash or vanilla js

I have an array of objects like:我有一系列对象,例如:

[
    {
        id: 8,
        name: 'Shirts',
        slug: 'shirts',
        parent_id: null
    },
    {
        id: 9,
        name: 'Pants',
        slug: 'pants',
        parent_id: null
    },
    {
        id: 10,
        name: 'Vintage Prints',
        slug: 'vintage-prints',
        parent_id: 8
    },
    {
        id: 11,
        name: 'Cotton Tee',
        slug: 'cotton-tee',
        parent_id: 8
    },
    {
        id: 12,
        name: 'Business Khakis',
        slug: 'business-khakis',
        parent_id: 9
    }
]

What I need is:我需要的是:

[
    {
        id: 9,
        name: 'Pants',
        slug: 'pants',
        parent_id: null
    },
    {
        id: 12,
        name: 'Business Khakis',
        slug: 'business-khakis',
        parent_id: 9
    },
    {
        id: 8,
        name: 'Shirts',
        slug: 'shirts',
        parent_id: null
    },
    {
        id: 11,
        name: 'Cotton Tee',
        slug: 'cotton-tee',
        parent_id: 8
    },
    {
        id: 10,
        name: 'Vintage Prints',
        slug: 'vintage-prints',
        parent_id: 8
    }
]

WHAT I'VE TRIED: This looks like it should work:我试过什么:这看起来应该有效:

_.orderBy(categories, ['parent_id', 'name'], ['asc', 'asc']);

But I wonder if the nulls in parent_id are messing with it.但我想知道 parent_id 中的空值是否在搞乱它。

EDIT: Inner and outer results should also be sorted alphabetically.编辑:内部和外部结果也应按字母顺序排序。 So Pants before shirts in the outer level and Cotton Tee before Vintage Prints in the child tier.因此,外层的裤子在衬衫之前,棉质 T 恤在儿童层的 Vintage Prints 之前。 Keep in mind that this can be infinite layers deep where Cotton Tee could be the parent and so on.请记住,这可以是无限深的层,其中 Cotton Tee 可能是父级等等。

It would also be great if each sorted object could receive an index or level so that you knew how many levels it was nested.如果每个排序的 object 都可以接收一个索引或级别,以便您知道它嵌套了多少级别,那就太好了。

A single sort does not work, because of the parent children relation, which is not considered while sorting the data.单一排序不起作用,因为父子关系在对数据进行排序时未考虑。

This solution works in three parts:该解决方案分为三个部分:

  1. Sorts data by alphabet, because the following tree is build in insertion order.按字母表对数据进行排序,因为以下树是按插入顺序构建的。

  2. Builds a tree with the given relationship.构建具有给定关系的树。

  3. Traverses the tree and gets the sorted flat data back.遍历树并返回排序后的平面数据。

 var data = [{ id: 8, name: 'Shirts', slug: 'shirts', parent_id: null }, { id: 9, name: 'Pants', slug: 'pants', parent_id: null }, { id: 10, name: 'Vintage Prints', slug: 'vintage-prints', parent_id: 8 }, { id: 11, name: 'Cotton Tee', slug: 'cotton-tee', parent_id: 8 }, { id: 12, name: 'Business Khakis', slug: 'business-khakis', parent_id: 9 }].sort(function (a, b) { return a.name.localeCompare(b.name); }), tree = function (data, root) { var r = [], o = {}; data.forEach(function (a) { o[a.id] = { data: a, children: o[a.id] && o[a.id].children }; if (a.parent_id === root) { r.push(o[a.id]); } else { o[a.parent_id] = o[a.parent_id] || {}; o[a.parent_id].children = o[a.parent_id].children || []; o[a.parent_id].children.push(o[a.id]); } }); return r; }(data, null), sorted = tree.reduce(function traverse(r, a) { return r.concat(a.data, (a.children || []).reduce(traverse, [])); }, []) console.log(sorted); console.log(tree);
 .as-console-wrapper { max-height: 100%;important: top; 0; }

Extended version with level:带级别的扩展版本:

 var data = [{ id: 8, name: 'Shirts', slug: 'shirts', parent_id: null }, { id: 9, name: 'Pants', slug: 'pants', parent_id: null }, { id: 10, name: 'Vintage Prints', slug: 'vintage-prints', parent_id: 8 }, { id: 11, name: 'Cotton Tee', slug: 'cotton-tee', parent_id: 8 }, { id: 12, name: 'Business Khakis', slug: 'business-khakis', parent_id: 9 }].sort(function (a, b) { return a.name.localeCompare(b.name); }), tree = function (data, root) { var r = [], o = {}; data.forEach(function (a) { o[a.id] = { data: a, children: o[a.id] && o[a.id].children }; if (a.parent_id === root) { r.push(o[a.id]); } else { o[a.parent_id] = o[a.parent_id] || {}; o[a.parent_id].children = o[a.parent_id].children || []; o[a.parent_id].children.push(o[a.id]); } }); return r; }(data, null), sorted = tree.reduce(function traverse(level) { return function (r, a) { a.data.level = level return r.concat(a.data, (a.children || []).reduce(traverse(level + 1), [])); }; }(0), []); console.log(sorted);
 .as-console-wrapper { max-height: 100%;important: top; 0; }

This is my solution这是我的解决方案

It handles multiple nested elements.它处理多个嵌套元素。

I got a solution I use lodash for array equality but you can implement it in pure JS also我得到了一个解决方案,我使用 lodash 来实现数组相等性,但你也可以在纯 JS 中实现它

const sortByLevel = (
  array: ({ id: string, parent_id: string, level: number })[],
  i: number = 0,
): ({ id: string, parent_id: string, level: number })[] => {
  const sortedArray = array
    .map((c) => ({ ...c, level: array.findIndex((a) => a.id === c.parent_id) }))
    .sort((a, b) => a.level - b.level);

  if (
    _.isEqual(
      sortedArray.map((c) => c.id),
      array.map((c) => c.id),
    )
  )
    return sortedArray;

  return sortByLevel(sortedArray, i + 1);
};

const sortByParent = (array: { id: string, parent_id: string }[]): { id: string, parent_id: string }[] => {
  return sortByLevel(
    array
      .map((c) => ({ ...c, level: array.findIndex((a) => a.id === c.parent_id) }))
      .sort((a, b) => a.level - b.level),
    1,
  ).map((c) => {
    const { level, ...card } = c;
    return card;
  });
};

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

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