使用id和parentid创建层次结构

时间:2017-05-22 01:03:18

标签: javascript

可能重复,但这个与其他问题不同,不希望将对象挂钩到另一个。我想创建一个包含层次结构数据的新对象/数组

所以我有这些数据集:

var sets = {
    'dataSet1': {
        'ID': '1',
        'ParentID': '0'
    },

    'dataSet2': {
        'ID': '2',
        'ParentID': '1'
    },

    'dataSet3': {
        'ID': '3',
        'ParentID': '1'
    },

     'dataSet4': {
         'ID': '4',
         'ParentID': '3'
    }
}

我想循环遍历它们并使用ID和ParentID动态创建层次结构。我希望输出的过程结果如下:

var tiers = {
     'tier1':[
     {
        'parentid':'0',
        'id':'1'
     }
     ]

    ,'tier2':[
     {
        'parentid':'1',
        'id':'2'
     },
     {
        'parentid':'1',
        'id':'3'
     }
     ]
    ,'tier3':[
     {
        'parentid':'3',
        'id':'4'
     }
     ]
}

基本上,层对象将包含包含具有id和父id字段的对象的层数组。

因此,当我访问数据时,我所要做的就是通过层[' tier2'] [1]获取它们。我需要做的就是将数据结构化为层次结构。注意:原生js

这是我目前的尝试,但这只适用于层次结构顶部的那个,第二层及以下是最大的问题

var datasetobj = sets;
var hierarchy = tiers;
for (x in datasetobj) {

        if (datasetobj[x]['ParentID'] == 0) {
            hierarchy['tier1'].push({
                'parentid': datasetobj[x]['ParentID'],
                'id': datasetobj[x]['ID']
            });
        }

    }

现在,如果我要通过限制程序可以处理的层数来使用静态方法,然后检查每个ID然后插入层,这是相当容易的。但这并不完全是OOP的做法。

2 个答案:

答案 0 :(得分:1)

for循环中if条件的问题。它只会处理顶级元素if (datasetobj[x]['ParentID'] == 0) only processes parent id 0

在动态创建层次结构数组并删除if条件后获取您的内容。做类似的事情:



      var tiers = {};
      var sets = {
        'dataSet1': {
            'ID': '1',
            'ParentID': '0'
        },
    
        'dataSet2': {
            'ID': '2',
            'ParentID': '1'
        },
    
        'dataSet3': {
            'ID': '3',
            'ParentID': '1'
        },
    
         'dataSet4': {
             'ID': '4',
             'ParentID': '3'
        }
    };
    var datasetobj = sets;
    var hierarchy = tiers;
    for (x in datasetobj) {
            if (datasetobj[x]['ParentID']) { 
                var idx = 'tier'+datasetobj[x]['ParentID'];
                if(!hierarchy[idx])
                    hierarchy[idx]=[];
                hierarchy[idx].push({
                    'parentid': datasetobj[x]['ParentID'],
                    'id': datasetobj[x]['ID']
                });
            }
        };
    console.log(tiers);




答案 1 :(得分:1)

您的问题需要遍历您的依赖关系树,同时跟踪深度。您可以使用使用递归和递增深度的简单depth-first-traversal来解决问题。

我不得不为缺乏解释而道歉。如果您以后需要,我会添加更多详细信息,但我必须运行。希望这有助于解决您的问题。如果您需要更多解释,请告诉我。

我还在层对象中添加了originalKey属性,因此您可以使用originalKey访问原始对象。

更新:好的,你想要更多的解释,你会得到更多的解释。我也改变了我的实现速度。

  

另外,您能提供一些链接,我可以在哪里学习这些概念吗?你的方法对我来说很新鲜

这个问题只能通过recursion和一些基本的计算机科学概念来解决。我将解释代码,并提供一些良好的计算机科学概念链接。这些概念并不复杂。这些是你在计算机科学本科课程中首先学到的东西。

sets转换为hashMap

我要做的第一件事就是将您的sets对象转换为另一个我正在调用hashMap的对象。我正在将它转换为这种形式,因为它会使"查找"我们创建tier对象时速度更快。

以下是sets对象供参考:

const sets = {
  'dataSet1': {
    'ID': '1',
    'ParentID': '0'
  },

  'dataSet2': {
    'ID': '2',
    'ParentID': '1'
  },

  'dataSet3': {
    'ID': '3',
    'ParentID': '1'
  },

  'dataSet4': {
    'ID': '4',
    'ParentID': '3'
  }
};

我执行此转换:

const hashMap = (Object
  // 1
  .keys(sets)
  // 2
  .map(datasetKey => ({
    originalKey: datasetKey,
    id: sets[datasetKey].ID,
    parentId: sets[datasetKey].ParentID
  }))
  // 3
  .reduce((hashMap, value) => {
    // 4
    const hashMapKey = 'hasParentId' + value.parentId;
    // 5
    if (hashMap[hashMapKey] === undefined) {
      hashMap[hashMapKey] = [];
    }
    // 6
    hashMap[hashMapKey].push(value);
    // 7
    return hashMap;
  }, {})
);

导致此对象:

{
  "hasParentId0": [
    {
      "originalKey": "dataSet1",
      "id": "1",
      "parentId": "0"
    }
  ],
  "hasParentId1": [
    {
      "originalKey": "dataSet2",
      "id": "2",
      "parentId": "1"
    },
    {
      "originalKey": "dataSet3",
      "id": "3",
      "parentId": "1"
    }
  ],
  "hasParentId3": [
    {
      "originalKey": "dataSet4",
      "id": "4",
      "parentId": "3"
    }
  ]
}

一步一步:

  1. 使用Object.keys从对象sets
  2. 获取一系列密钥
  3. 使用Array.prototype.map将数组创建的每个键从调用Object.keys映射到新对象。该对象有3个属性:originalKeyidparentId
  4. 使用Array.prototype.reduce 映射对象数组(在上一步中创建)缩减为单个对象。 Array.prototype.reduce的第一个参数是" reducer"带两个参数的函数。 " reducer"的第一个参数函数是一个"累加器"第二个参数是数组中的一个项目。 reducer将被多次调用,一次调用数组中的每个项目。 reducer函数返回一个累加器,该累加器将在下一个内部函数的调用中使用(因此数组的所有项都减少到累加器中)。 Array.prototype.reduce的第二个参数是初始累加器。在这种情况下,初始累加器是一个空对象{}
  5. 在reducer函数中,我们创建一个字符串hashMapKey。该字符串将用作累加器的键。
  6. 此转换期望每个hashMap[hashMapKey]都是一个数组。在这里,我们检查hashMap[hashMapKey]的值是否为数组,如果不是,我们将其设为一个。
  7. 现在我们知道hashMap[hashMapKey]处有一个数组,我们可以在该数组上推送value。请记住,value是我们从第2步映射的对象。
  8. 返回修改后的hashMap以用作"累加器"下一个"减速机"调用
  9. 缩减完成后,hashMap包含正确的转换。在下一步中快速查找需要转换为setshashMap

    hashMap转换为tier

    这就是计算机科学思想的用武之地。上面的结构有一个隐含的graph,其中每个节点(也就是点)都是来自sets的对象,每个边缘都是通过属性的有向关系ParentID

    为了创建您想要的结构,您需要执行depth-first-traversal遍历此图表,同时跟踪深度。

    以下是代码:

    // 1
    const tier = {};
    let maxDepth = 0;
    
    // 2
    function traverse(currentParentId, depth) {
      // 3
      if (depth > maxDepth) {
        maxDepth = depth;
      }
      // 4
      const childrenOfCurrentParent = hashMap['hasParentId' + currentParentId]
    
      // 5
      if (childrenOfCurrentParent === undefined) {
        return;
      }
    
      // 6
      childrenOfCurrentParent.forEach(value => {
        // 7
        const tierKey = 'tier' + depth;
        if (tier[tierKey] === undefined) {
          tier[tierKey] = [];
        }
        // 8
        tier[tierKey].push(value);
        // 9
        traverse(value.id, depth + 1);
      });
    }
    
    1. 创建一个空对象来存储层。 (可选)为保持最大深度创建变量。如果要将层对象转换为层数组,这非常有用。
    2. 定义一个函数traverse,它带有两个参数:currentParentIddepthcurrentParentId是用于标识当前父级的id的密钥。 此函数最初使用currentParentId 0depth 0来调用。一旦我们查看实现,这将更有意义代码。
    3. 重新分配maxDepth
    4. 从查找表中抓取childrenOfCurrentParent。看看我们为什么做到了?在此表中查找值非常快:)
    5. 如果没有childrenOfCurrentParent,请不要做任何事情。
    6. 使用Array.prototype.forEach遍历childrenOfCurrentParentvalue再次是转换中映射对象的value
    7. 创建tierKey以存储value。如果数组未初始化,请将其初始化。
    8. 推送value
    9. 使用recursion,再次从内部调用traverse功能,更改currentParrentId并递增depth。从第3步开始重复,直到没有更多项目被放置。这基本上是深度优先遍历
    10. tier转换为tierArray

      我认为这个是不言自明的。由于层是一组有序项,因此最好将其存储在数组中。这就是我们跟踪maxDepth

      的原因

      工作代码段

      
      
      const sets = {
        'dataSet1': {
          'ID': '1',
          'ParentID': '0'
        },
      
        'dataSet2': {
          'ID': '2',
          'ParentID': '1'
        },
      
        'dataSet3': {
          'ID': '3',
          'ParentID': '1'
        },
      
        'dataSet4': {
          'ID': '4',
          'ParentID': '3'
        }
      };
      
      // `hashMap` is a data transform
      // see the `console.log` of this transform
      const hashMap = (Object
        // grab the keys of the object `sets`
        .keys(sets)
        // then map every key to a new object
        .map(datasetKey => ({
          originalKey: datasetKey,
          id: sets[datasetKey].ID,
          parentId: sets[datasetKey].ParentID
        }))
        // reduce the new objects into a single object
        .reduce((hashMap, value) => {
          // create a key to store the `value`
          const hashMapKey = 'hasParentId' + value.parentId;
          // if `hashMap[hashMapKey]` isn't intialized yet,
          // initialize it to an array
          if (hashMap[hashMapKey] === undefined) {
            hashMap[hashMapKey] = [];
          }
          // then push the value (the objects created above)
          hashMap[hashMapKey].push(value);
          // return the hashMap
          return hashMap;
        }, {})
      );
      
      console.log('hashMap', hashMap);
      
      // create an object to store the `tier`s in.
      const tier = {};
      // keep track of the `maxDepth` in case you want to transform `tier` to an array
      let maxDepth = 0;
      
      // this is a recursive function to traverse your graph
      // this implements a depth-first-traversal keeping track of the `depth`
      function traverse(currentParentId, depth) {
        if (depth > maxDepth) {
          maxDepth = depth;
        }
        const childrenOfCurrentParent = hashMap['hasParentId' + currentParentId]
      
        // if there are no children of the currentParent
        if (childrenOfCurrentParent === undefined) {
          return;
        }
      
        childrenOfCurrentParent.forEach(value => {
          const tierKey = 'tier' + depth;
          if (tier[tierKey] === undefined) {
            tier[tierKey] = [];
          }
          tier[tierKey].push(value);
          traverse(value.id, depth + 1);
        });
      }
      
      // call the function to start the traversal
      // using 0 as the first `currentParentId` and
      // using 0 as the first `depth` value
      traverse(0, 0);
      
      
      // the tier object is now created
      console.log('tier', tier);
      
      // but it might be more natural to have `tier` as an array
      const tierArray = [];
      for (let i = 0; i < maxDepth; i += 1) {
        tierArray[i] = tier['tier' + i];
      }
      
      console.log('tierArray', tierArray);
      &#13;
      &#13;
      &#13;

      P,嗯,这是什么答案。希望这有帮助!