【问题标题】:Lodash - How to create a tree from a flat arrayLodash - 如何从平面数组创建树
【发布时间】:2018-12-17 16:06:36
【问题描述】:

每次我的应用程序加载时,我都会收到以下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'
  }
]

树级别由identifier 属性标识。

这棵树可以有数千个孩子,所以它需要递归。

如何使用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'
      }
    ]
  }
]

【问题讨论】:

  • 数据排序了吗?你试过什么?
  • @NinaScholz 步骤的顺序可以是随机的...我还没有尝试过,因为我不知道如何处理identifier 属性
  • 您是否在控制数据源?一个不同的模式会使处理这个数组更容易——“标识符”属性是一个实际的唯一数字,并添加一个“父标识符”属性来引用父对象的标识符。
  • @aardvark 我无法控制数据源。这是我正在使用的现成 API =/

标签: javascript json lodash


【解决方案1】:

您可以通过在identifier 的同一路径中查找对象并构建嵌套结构来采取迭代方法。

这种方法也适用于未排序的数据。

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

【讨论】:

  • 代码有效!但是,你能解释一下这里发生了什么吗?如果可能的话,通过 de 标识符订购 tree
  • 我应该解释哪一部分?对于排序结果,您可以提前按identifier 对平面数组进行排序。
【解决方案2】:

我使用previous answer of mine 作为基础。有很多相似之处,但你的“路径”语法有点不同,我不得不调整一些解析。

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

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


注意事项:

原始答案中的相同警告也适用于此。

【讨论】:

    【解决方案3】:

    您可以使用Array.reduce()_.setWith() 按路径(身份)创建对象树。然后您可以使用带有_.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>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-19
      • 1970-01-01
      • 2020-08-22
      • 1970-01-01
      • 2016-09-25
      • 1970-01-01
      相关资源
      最近更新 更多