【问题标题】:Creating a specific tree structure from materialized path从物化路径创建特定的树结构
【发布时间】:2020-02-20 18:53:43
【问题描述】:

我有“实体”和“实体模板”。顶级实体与“templateId1”实体模板相关联。从那里,实体通过使用其 id 与路径中的另一个实体绑定。我试图以一种有效的方式结束以下数据结构:

const entities = [
  {
    id: 'xi32',
    name: 'Some name',
    path: ',templateId1,'
  },
  {
    id: 'x382',
    name: 'Some name 2',
    path: ',templateId1,xi32,'
  },
  {
    id: '2oxwo',
    name: 'Some name 3',
    path: ',templateId1,xi32,x382,'
  },
  {
    id: '2',
    name: '2',
    path: ',templateId1,'
  },
  {
    id: '2-2',
    name: '2-2',
    path: ',templateId1,2,'
  },
  {
    id: '3-3',
    name: '3-3',
    path: ',templateId1,3,'
  },
  {
    id: '3-3-3',
    name: '3-3-3',
    path: ',templateId1,3,3-3,'
  },
  {
    id: '3-3-3-3',
    name: '3-3-3-3',
    path: ',templateId1,3,3-3,3-3-3,'
  },
  {
    id: '3',
    name: '3',
    path: ',templateId1,'
  }
];

const desiredResult = [
      {
        id: 'xi32',
        name: 'Some name',
        path: ',templateId1,',
        children: [
          {
            id: 'x382',
            name: 'Some name 2',
            path: ',templateId1,xi32,',
            children: [
              {
                id: '2oxwo',
                name: 'Some name 3',
                path: ',templateId1,xi32,x382,',
                children: null
              }
            ]
          }
        ]
      },
      {
        id: '2',
        name: '2',
        path: ',templateId1,',
        children: [
          {
            id: '2-2',
            name: '2-2',
            path: ',templateId1,2,',
            children: null
          }
        ]
      },
      {
        id: '3',
        name: '3',
        path: ',templateId1,',
        children: [
          {
            id: '3-3',
            name: '3-3',
            path: ',templateId1,3,',
            children: [
              {
                id: '3-3-3',
                name: '3-3-3',
                path: ',templateId1,3,3-3,',
                children: [
                  {
                    id: '3-3-3-3',
                    name: '3-3-3-3',
                    path: ',templateId1,3,3-3,3-3-3,',
                    children: null
                  }
                ]
              }
            ]
          }
        ]
      }
    ];

结构的最初灵感来自 MongoDB 文档:

https://docs.mongodb.com/manual/tutorial/model-tree-structures-with-materialized-paths/

我有一个稍微不同的用例,“实体模板”是顶级父级,但在“实体”中,用例是相同的。非常感谢任何见解。

【问题讨论】:

  • 您想在 MongoDB 中还是在 JavaScript 中完成它?
  • @mickl 老实说我不确定。如果可以的话,我喜欢在数据库级别做事,因为它通常性能更高,但特别是在这两种情况下都能做到这一点会很好。这样 API 可以在需要时将其返回,并且可以在代码中创建树。
  • @MisterJojo 我尝试使用这个stackoverflow.com/questions/6232753/…,但它采用不同的结构,老实说,我很难解析其中发生的事情。我还在玩弄那个代码。
  • @MisterJojo 我的错,这是一个错误。我会编辑它。
  • @MisterJojo 我想是的,但我将它按“路径”排序,所以最短的路径排在第一位。假设它可以按任何顺序排列会很好。为了安全起见,我在代码中再次对其进行排序。

标签: javascript arrays mongodb materialized-path-pattern


【解决方案1】:

我已经这样做了... 我添加了一个排序来防止任何关键的混乱。

const data = 
      [ { id: '1',     name: '1',     path: ',templateId1,'      } 
      , { id: '2',     name: '2',     path: ',templateId1,'      } 
      , { id: '2-1',   name: '2-1',   path: ',templateId1,2,'    } 
      , { id: '1-1',   name: '1-1',   path: ',templateId1,1,'    } 
      , { id: '1-1-1', name: '1-1-1', path: ',templateId1,1,1-1,'} 
      ]
let result  = []
  , parents = [ {children: result} ]
  ;
for (let elData of data.sort((a,b)=>a.id.localeCompare(b.id)))
  {
  let newEl = { ...elData, children: null }
    , level = (elData.id.match(/-/g)||'').length // count number of '-' 
    ;
  if (parents[level].children===null)
    { parents[level].children = [] }
  parents[level].children.push( newEl ) 
  parents[++level] = newEl
  }
  
console.log(JSON.stringify(result,0,2 )  )  // for testing
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 这看起来很棒。比我现在拥有的要短得多。它需要稍微调整一下,因为关系/深度不是基于“id”或“-”,而是基于“路径”。我现在正在摆弄它。
  • 应该是 level = (elData.path.match(/,/g) || '').length - 2; (-2)是去掉字符串开头和结尾的“,”。
  • 是的,男人的作品很棒!通过了测试以及我在评论中所做的更改。非常感谢:)。
  • 我将完成我的解决方案,我也会发布它,但我喜欢你的。
  • 是的,这是我能看到的让它工作的唯一方法。 id 可以是任何东西。我只是使用“-”作为示例。我拥有的真实身份是“5e4dcc5e8b660466ca1fe916”。所以我们不能从 id 中寻找任何可靠的深度/级别度量,这就是你想要达到的目标吗?
【解决方案2】:

最后我设法编写了这个代码,调试了很多,因为有很多陷阱。

这个想法是让 json 分几次传递,每次计算剩余元素的数量。

如果这个数字没有从一个传递到另一个传递,那么 pgm 发送一个错误并停止。

对于要添加的每个元素,我们计算假定的父元素 getParentKey(),它返回一个包含其所有父元素列表的表格

然后您必须在表中找到直接父级,使用此列表,从根开始,如果父级不存在,则该元素保留在表中,然后将重试。

const entities = 
      [ { id: 'xi32',    name: 'Some name',   path: ',templateId1,'            } 
      , { id: 'x382',    name: 'Some name 2', path: ',templateId1,xi32,'       } 
      , { id: '2oxwo',   name: 'Some name 3', path: ',templateId1,xi32,x382,'  } 
      , { id: '2',       name: '2',           path: ',templateId1,'            } 
      , { id: '2-2',     name: '2-2',         path: ',templateId1,2,'          } 
      , { id: '3-3',     name: '3-3',         path: ',templateId1,3,'          } 
      , { id: '3-3-3',   name: '3-3-3',       path: ',templateId1,3,3-3,'      } 
      , { id: '3-3-3-3', name: '3-3-3-3',     path: ',templateId1,3,3-3,3-3-3,'} 
      , { id: '3',       name: '3',           path: ',templateId1,'            } 
      ]; 
const Result  = []
  ;
let unDone = []
  , source = entities
  ;
let cpt = 0  // just for stoping infinite loop on error
  ;
do
  {
  unDone = setResult( source, unDone.length )
  source = unDone;
  if (++cpt > 10) throw 'mince! something is rotten in the state of Denmark...'
  }
while
  (unDone.length>0)
  ;   
/* --------------------------------------------------------*/
console.log( 'result===', JSON.stringify(Result,0,2 ) )
/* --------------------------------------------------------*/

function setResult( arrayIn, nb_rej )
  {
  let orphans = [];
  for (let elData of arrayIn)
    {
    let newEl = { ...elData, children: null }  
      , parAr = getParentKey(elData.path)

    if (parAr.length===0)
      { Result.push(newEl) }
    else
      {
      let resParent = Result;
      do
        {
        let rech = parAr.pop()
          , fPar = resParent.find(treeElm=>(treeElm.id===rech.id && treeElm.path===rech.path))
          ;
        if (fPar) 
          {
          if (fPar.children===null)
            fPar.children = []
            ;
          resParent = fPar.children
          }
        else  //  throw `parent element not found : id:'${rech.id}', path:'${rech.path}'`  ;
          {
          orphans.push( { ...elData }   )
          resParent = null
          parAr.length = 0
          }  
        }
      while
        (parAr.length>0)
        ;
      if (resParent) resParent.push(newEl);
      }
    }
  if ( orphans.length>0 && orphans.length == nb_rej )
    throw ` ${nb_rej} children element(s) without parent !'`;

  return orphans
  }

function getParentKey( path )
  {                          // return array of parent element
  let rep = []
    , par = path
    , lev, bKey, xCom, idK;
  do
    {
    bKey = par.substring(0, par.lastIndexOf(','))  // remove last ','
    lev  = bKey.match(/,/g).length -1
    if (lev>0)
      {
      xCom = bKey.lastIndexOf(',')
      par  = bKey.substring(0, xCom) +',' 
      idK  = bKey.substring(++xCom)
      rep.push({ path:par, id:idK })
    } }
  while
    (lev>0)
  return rep
  }
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 这太棒了!谢谢 :)。我正在通读并试图理解代码,但它通过了所有测试。我也有一个解决方案,但我被困在与孩子们一起更新树上。我也可能会完成并发布它,但这可能有效。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-23
  • 2020-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多