简体   繁体   English

Lodash-如何从平面数组创建树

[英]Lodash - How to create a tree from a flat array

Every time my application loads, I receive the following json : 每当我的应用程序加载时,我都会收到以下json

[
  {
    id: 'mALRRY93jASr',
    identifier: '100',
    text: 'Text A'
  },
  {
    id: '7S3xHZEdNcfV',
    identifier: '200',
    text: 'Text B'
  },
  {
    id: '2ZA5xSJeukU6',
    identifier: '300',
    text: 'Text C',
  },
  {
    id: 'bhg3GnLEvw2k',
    identifier: '300.100',
    text: 'Text C - A'
  },
  {
    id: 'bhg3GnLEvw2k',
    identifier: '300.100.100',
    text: 'Text C - A - A'
  },
  {
    id: '2AcXNr4HT388',
    identifier: '300.200',
    text: 'Text C - B'
  }
]

The tree levels are identified by the identifier property. 树级别由identifier属性identifier

The tree can have thousands of children, so it needs to be recursive. 该树可以有数千个孩子,因此它需要递归。

How can I arrange the json using Lodash to looks like the following json ? 我如何使用Lodash排列json使其看起来像以下json

[
  {
    id: 'mALRRY93jASr',
    identifier: '100',
    text: 'Text A'
  },
  {
    id: '7S3xHZEdNcfV',
    identifier: '200',
    text: 'Text B'
  },
  {
    id: '2ZA5xSJeukU6',
    identifier: '300',
    text: 'Text C',
    children: [
      {
        id: 'bhg3GnLEvw2k',
        identifier: '300.100',
        text: 'Text C - A',
        children: [
          {
            id: 'bhg3GnLEvw2k',
            identifier: '300.100.100',
            text: 'Text C - A - A'
          }
        ]
      },
      {
        id: '2AcXNr4HT388',
        identifier: '300.200',
        text: 'Text C - B'
      }
    ]
  }
]

You could take an iterative approach by looking for objects in the same path of identifier and build a nested structure. 您可以通过在同一identifier路径中查找对象并构建嵌套结构来采用迭代方法。

This approach works for unsorted data as well. 这种方法也适用于未排序的数据。

 var data = [{ id: 'mALRRY93jASr', identifier: '100', text: 'Text A' }, { id: '7S3xHZEdNcfV', identifier: '200', text: 'Text B' }, { id: '2ZA5xSJeukU6', identifier: '300', text: 'Text C' }, { id: 'bhg3GnLEvw2k', identifier: '300.100', text: 'Text C - A' }, { id: 'bhg3GnLEvw2k', identifier: '300.100.100', text: 'Text C - A - A' }, { id: '2AcXNr4HT388', identifier: '300.200', text: 'Text C - B' }], tree = []; data.reduce((r, o) => { o.identifier .split('.') .map((_, i, a) => a.slice(0, i + 1).join('.')) .reduce((q, identifier, i, { length }) => { var temp = (q.children = q.children || []).find(p => p.identifier === identifier); if (!temp) { q.children.push(temp = { identifier }); } if (i + 1 === length) { Object.assign(temp, o); } return temp; }, r); return r; }, { children: tree }); console.log(tree); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

I used a previous answer of mine as a base. 我以我以前的答案为基础。 There are a lot of similarities, but your "path" syntax is a little different and I had to tweak some of the parsing. 有很多相似之处,但是您的“路径”语法有点不同,因此我不得不调整一些解析。

const data = [
  {
    id: 'mALRRY93jASr',
    identifier: '100',
    text: 'Text A'
  },
  {
    id: '7S3xHZEdNcfV',
    identifier: '200',
    text: 'Text B'
  },
  {
    id: '2ZA5xSJeukU6',
    identifier: '300',
    text: 'Text C',
  },
  {
    id: 'bhg3GnLEvw2k',
    identifier: '300.100',
    text: 'Text C - A'
  },
  {
    id: 'bhg3GnLEvw2k',
    identifier: '300.100.100',
    text: 'Text C - A - A'
  },
  {
    id: '2AcXNr4HT388',
    identifier: '300.200',
    text: 'Text C - B'
  }
];

const pathPartRegex = /.*?\./g;
const tree = _.reduce(data, (result, value) => {
    // We use the . character as a "path part" terminator,
    // but it is not there at the end of the string, so we add it
    let identifier = value.identifier;
    if (!identifier.endsWith(".")) {
        identifier = identifier + ".";
    }
    const pathParts = identifier.match(pathPartRegex);
    let node = result;
    let path = "";

    // Go down through tree until last path part
    const notLastPart = pathParts.splice(0, pathParts.length - 1);
    for (const pathPart of notLastPart) {
        path += pathPart;
        const existingNode = node.children 
                                ? node.children.find(item => path.startsWith(item.identifier) )
                                : node.find(item => path.startsWith(item.identifier));
        if (existingNode) {
            node = existingNode
        } else {
            // If we need to traverse over a path that doesn't exist, just create it
            // See notes 
            const newNode = {
                identifier: path,
                children: []
            };

            // The root element is just an array, and doesn't have a children property
            if (node.children) {
                node.children.push(newNode);
            } else {
                node.push(newNode);
            }
            node = newNode;
        }
    }

    // Add new node
    const newNode = {
        id: value.id,
        text: value.text,
        identifier: value.identifier,
        children: []
    };

    // The root element is just an array, and doesn't have a children property
    if (node.children) {
        node.children.push(newNode);
    } else {
        node.push(newNode);
    }

    return result;
}, []);

Tested via RunKit ( https://npm.runkit.com/lodash ) 通过RunKit( https://npm.runkit.com/lodash )测试


Notes: 笔记:

The same warnings from the original answer also apply here. 原始答案中的相同警告也适用于此。

You can use Array.reduce() and _.setWith() to create a tree of objects by the path (identity). 您可以使用Array.reduce()_.setWith()通过路径(身份)创建对象树。 Then you can use a recursive function with _.transform() to convert the children to an array using _.values() : 然后你可以使用一个递归函数_.transform()的转换children使用到一个数组_.values()

 const createTree = (arr) => { // reduce to a tree of objects const oTree = arr.reduce((r, o) => { const key = o.identifier.replace(/\\./g, '.children.'); // creates the path and adds the object value return _.setWith(r, key, o, Object) }, {}); // transforms the children to an array recursivly const transformChildren = (tree) => _.transform(tree, (acc, v, k) => { const value = _.isObject(v) ? transformChildren(v) : v; acc[k] = _.eq(k, 'children') ? _.values(value) : value; }); return transformChildren(_.values(oTree)); }; const data = [{"id":"mALRRY93jASr","identifier":"100","text":"Text A"},{"id":"7S3xHZEdNcfV","identifier":"200","text":"Text B"},{"id":"2ZA5xSJeukU6","identifier":"300","text":"Text C"},{"id":"bhg3GnLEvw2k","identifier":"300.100","text":"Text C - A"},{"id":"bhg3GnLEvw2k","identifier":"300.100.100","text":"Text C - A - A"},{"id":"2AcXNr4HT388","identifier":"300.200","text":"Text C - B"}]; const result = createTree(data); console.log(result); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script> 

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

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