【问题标题】:get array of parent and all of its child获取父数组及其所有子数组
【发布时间】:2016-04-15 15:37:59
【问题描述】:

假设我有这种数据...

data = [{
    "_id" : "1",
    "parentId" : "thisPostId",
    "topLevelId" : "1",
    "text" : "<p>comment</p>",
},
{
    "_id" : "2",
    "parentId" : "1",
    "topLevelId" : "1",
    "text" : "<p>reply to comment</p>",
},
{
    "_id" : "3",
    "parentId" : "2",
    "topLevelId" : "1",
    "text" : "<p>reply to reply to comment</p>",
},
{
    "_id" : "4",
    "parentId" : "3",
    "topLevelId" : "1",
    "text" : "<p>reply to reply to reply to comment</p>",
}]

我需要删除评论及其所有子评论...

如果要删除的评论是_id:1,,那么我需要一个["1","2","3","4"],, 的数组,然后我可以运行Coll.remove({_id:{$in:["1","2","3","4"]}}, callback);

如果要删除的评论是_id:2,那么我需要一个["2","3","4"]数组

如果要删除的评论是_id:3,那么我需要一个["3","4"]数组

如果要删除的评论是_id:4,那么我需要一个["4"]数组

我试过这个(不知道)...

_.forEach(data, function(value, key){
    _.pluck(_.where(key, { "parentId" : "2" }), '_id');
});

而且不工作...

任何有关 javascript/lodash/underscore 的帮助将不胜感激,,,

谢谢你...

【问题讨论】:

  • hii @Rajesh 感谢您的回复,,,文本键只是为了更好地理解,,只是忽略......我们应该关注的是_idparentId
  • 这看起来像XY problem,你好像想级联删除一个文档:stackoverflow.com/questions/14348516/…
  • hii @ShanShan 谢谢你的回复,,,我正在用流星做项目,这似乎行不通,因为流星有自己的名为 minimongo 的 mongo 驱动程序,但是谢谢你,,以前不知道猫鼬有这样的功能
  • meteorjs 具有实现相同功能的钩子。您应该发布一个关于您遇到的具体问题的问题,流星用户应该能够提供帮助。

标签: javascript arrays underscore.js lodash


【解决方案1】:

这是一个带有临时对象和对 id 的递归调用的提案。

临时对象 o 包含所有 id 及其子代

{
    "1": ["2"],
    "2": ["3"],
    "3": ["4"],
    "thisPostId": ["1"]
}

构建此对象后,将获取用于查找的 id 并检查该对象是否包含该属性。虽然所有人员都是数组,但可以遍历 go() 并获取所有 id 以进行收集。如果有另一个孩子,则递归迭代正在进行。

var data = [{ "_id": "1", "parentId": "thisPostId", "topLevelId": "1", "text": "<p>comment</p>", }, { "_id": "2", "parentId": "1", "topLevelId": "1", "text": "<p>reply to comment</p>", }, { "_id": "3", "parentId": "2", "topLevelId": "1", "text": "<p>reply to reply to comment</p>", }, { "_id": "4", "parentId": "3", "topLevelId": "1", "text": "<p>reply to reply to reply to comment</p>", }];

function getConnected(s) {
    function go(a) { r.push(a); o[a] && o[a].forEach(go); }

    var o = data.reduce(function (r, a) {
            r[a.parentId] = r[a.parentId] || [];
            r[a.parentId].push(a._id);
            return r;                
        }, {}),
        r = [s];

    o[s] && o[s].forEach(go);
    return r;
}

for (var i = 1; i <= 4; i++) {
    document.write('"' + i + '": ' + JSON.stringify(getConnected(i.toString())) + '<br>');
}

【讨论】:

  • heloo @NinaScholz .... 请详细说明您为什么建议使用临时对象和递归调用..??谢谢你...
  • hii,, @NinaScholz 您的代码在拥有大数据时似乎很有效,,, 我不知道为什么...有评论..??无论如何,对我进一步学习的临时对象有很好的参考..??非常感谢你h,,,
  • @KarinaL,由于哈希表,它很有效。所以查找时间很短。此处介绍的其他解决方案缺少该解决方案。
【解决方案2】:

在 OP 的 cmets 中,您说您正在使用 meteorjs 并且您似乎想要级联删除文档。 Meteorjs hooks 轻松实现:

var idToRemove;
Coll.remove({ _id: idToRemove }, callback);

// what to do after removing a Coll document
Coll.after.remove(function (userId, doc) {
    Coll.remove({ parentId: doc._id });
});

您需要先安装collection-hooks 包。

【讨论】:

  • hii @ShanShan 谢谢你的回答,,,为了其他目的,我需要从客户端获取一个数组,,,这就是为什么我想要客户端操作,,,但是谢谢你,,,
【解决方案3】:

首先,您需要一个函数从与搜索 id 匹配的对象中获取 topLevelId

function getTLID(searchId) {
  return data.filter(function(el) {
    return el._id === searchId;
  })[0].topLevelId;
}

使用reduce:将每个对象的_id 添加到具有该搜索ID的返回数组并且具有搜索ID具有@987654330 @大于等于搜索id,使用map抓取_ids。

function getIdArray(searchId) {
  var tlid = getTLID(searchId);
  return data.reduce(function (p, c) {
    var matchSearchId = +c.parentId >= +searchId || c._id === searchId;
    if (c.topLevelId === tlid && matchSearchId) p.push(c._id);
    return p;
  }, []).sort();
}

getIdArray('1') // [ "1", "2", "3", "4" ]
getIdArray('2') // [ "2", "3", "4" ]
getIdArray('3') // [ "3", "4" ]
getIdArray('4') // [ "4" ]

DEMO

如果你不喜欢reduce,可以使用filtermap

function getIdArray(searchId) {
  var tlid = getTLID(searchId);
  return data.filter(function(el) {
    var matchSearchId = +el.parentId >= +searchId || el._id === searchId;
    return el.topLevelId === tlid && matchSearchId;
  }).map(function(el) {
    return el._id;
  }).sort();
}

DEMO

【讨论】:

    【解决方案4】:

    这是一个相当长的使用递归,

    function getIDs(arr, id) {
    arr = arr || data;
    var ret = [];
    for (var i = 0; i < arr.length; i++) {
        var item = arr[i];
        if (item.parentId == id || item._id == id) {
            if (ret.indexOf(item._id) < 0) {
                ret.push(item._id);
                var newret = []
                for (var x = 0; x < arr.length; x++) {
                    if (x != i) newret.push(arr[x]);
                }
                var children = getIDs(newret, item._id);
                if (children.length > 0) {
                    for (var j = 0; j < children.length; j++) {
                        if (!(ret.indexOf(children[j]) >= 0)) { ret.push(children[j]); }
                    }
                }
            }
    
        }
    }
    return ret;
    

    }

    它的工作原理是获取所需父项的 id,然后获取其子项及其子项的子项的 id,它可以整天这样做......

    【讨论】:

    • 这是 imo 的最佳解决方案。它不关心数据的顺序。它会遍历子节点的子节点。虽然大型数据集的影响可能是一个问题。对于较小的数据,它看起来是最纯粹的解决方案。
    【解决方案5】:

    首先,您需要获取提及_id 的项目的索引,如果array 中存在项目,则可以使用array.splice 从提及的索引中删除n 元素。要从deleted 节点获取项目,数组的deepcopy 存储在tempory 变量中。

    splice() 方法通过删除现有元素和/或添加新元素来更改数组的内容。

    您可以使用data.length - index计算删除计数

    var data = [{
      "_id": "1",
      "parentId": "thisPostId",
      "topLevelId": "1",
      "text": "<p>comment</p>",
    }, {
      "_id": "2",
      "parentId": "1",
      "topLevelId": "1",
      "text": "<p>reply to comment</p>",
    }, {
      "_id": "3",
      "parentId": "2",
      "topLevelId": "1",
      "text": "<p>reply to reply to comment</p>",
    }, {
      "_id": "4",
      "parentId": "3",
      "topLevelId": "1",
      "text": "<p>reply to reply to reply to comment</p>",
    }];
    var getIndex = function(_id) {
      for (var i = 0; i < data.length; i++) {
        if (data[i]._id == _id) {
          return i;
        }
      }
    };
    
    function deepCopy(obj) {
     if (null == obj || "object" != typeof obj) return obj;
      var copy = obj.constructor();
      for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
      }
      return copy;
    }
    var _id = 1;
    
    var index = getIndex(_id);
    var _temp = deepCopy(data);
    var removedData = data.splice(index, 1);
    alert(removedData);
    if (typeof index !== 'undefined') {
      var neededData = _temp.splice(index, (_temp.length - index));
      alert(neededData);
    }

    Fiddle here

    【讨论】:

    • 如果数组中 cmets 的顺序发生变化,这将不起作用
    • 实际上@Karina_L 所问的恰恰相反
    • @Venugopal,是的!没有考虑到这一点。没找到你 StefanoSaitta
    • 她想删除孩子,但通过这个实现,她得到了孩子。 @RayonDabre 看看输出。
    • 为什么 cmets 会出现故障
    【解决方案6】:

    这是另一种解释,使用原生的Array.prototype.reduce 方法仅将子元素添加到返回的数组中。

    编辑,没有正确阅读问题,这将返回当前 id 和所有孩子。

    var data = [{
        "_id" : "1",
        "parentId" : "thisPostId",
        "topLevelId" : "1",
        "text" : "<p>comment</p>",
    },
    {
        "_id" : "2",
        "parentId" : "1",
        "topLevelId" : "1",
        "text" : "<p>reply to comment</p>",
    },
    {
        "_id" : "3",
        "parentId" : "2",
        "topLevelId" : "1",
        "text" : "<p>reply to reply to comment</p>",
    },
    {
        "_id" : "4",
        "parentId" : "3",
        "topLevelId" : "1",
        "text" : "<p>reply to reply to reply to comment</p>",
    }];
    
    function getChildIds( arr, id ){
      var parentFound = false;
      return arr.reduce(function( ret, item ){
        if( parentFound === false && item._id == id ){
          parentFound = true;
        } 
        
        if( parentFound ) {
          ret = ret.concat( item._id );
        }
        
        return ret;
      }, []);
    }
    
    console.log( getChildIds(data, '1') );
    console.log( getChildIds(data, '2') );
    console.log( getChildIds(data, '3') );
    console.log( getChildIds(data, '4') );
    &lt;script src="http://codepen.io/synthet1c/pen/WrQapG.js"&gt;&lt;/script&gt;

    任何顺序,不知道为什么需要考虑。

    var data = [{
      "_id": "2",
      "parentId": "1",
      "topLevelId": "1",
      "text": "<p>reply to comment</p>",
    }, {
      "_id": "1",
      "parentId": "thisPostId",
      "topLevelId": "1",
      "text": "<p>comment</p>",
    }, {
      "_id": "4",
      "parentId": "3",
      "topLevelId": "1",
      "text": "<p>reply to reply to reply to comment</p>",
    }, {
      "_id": "3",
      "parentId": "2",
      "topLevelId": "1",
      "text": "<p>reply to reply to comment</p>",
    }];
    
    function getChildIdsInAnyOrder(arr, id) {
      return arr.reduce(function(ret, item) {
        if ( parseInt(item._id) >= parseInt(id) ) {
          ret = ret.concat(item._id);
        }
        return ret;
      }, []);
    }
    
    console.log(getChildIdsInAnyOrder(data, '1'));
    console.log(getChildIdsInAnyOrder(data, '2'));
    console.log(getChildIdsInAnyOrder(data, '3'));
    console.log(getChildIdsInAnyOrder(data, '4'));
    &lt;script src="http://codepen.io/synthet1c/pen/WrQapG.js"&gt;&lt;/script&gt;

    【讨论】:

    • 与@rayon 的回答stackoverflow.com/a/34719952/1398867有什么不同
    • 它不那么复杂,流程更少,但我无法告诉你哪个更高效,[].reduce 每次迭代都使用函数,所以可能会更慢。
    • 如果数组的顺序发生变化,这并没有找到父id的所有子代。
    • 这是什么意思?生产中的 id 是什么,它们是整数吗,为什么数组的顺序会改变。我将添加另一个功能,但您需要解释您的实际问题
    • jsfiddle.net/LLfkop49 如果数据没有排序怎么办?并且孩子存在于其父母之前的索引中?它永远不会被删除
    【解决方案7】:

    你可以试试这样的:

    代码

    JSFiddle

    var data = [{
      "_id": "1",
      "parentId": "thisPostId",
      "topLevelId": "1",
      "text": "<p>comment</p>",
    }, {
      "_id": "2",
      "parentId": "1",
      "topLevelId": "1",
      "text": "<p>reply to comment</p>",
    }, {
      "_id": "3",
      "parentId": "2",
      "topLevelId": "1",
      "text": "<p>reply to reply to comment</p>",
    }, {
      "_id": "4",
      "parentId": "3",
      "topLevelId": "1",
      "text": "<p>reply to reply to reply to comment</p>",
    }];
    
    function getDependentList(id) {
      var retList = [];
    
      data.forEach(function(item) {
        if (item.parentId == id)
          retList.push(item["_id"]);
      });
    
      if (retList.length > 0) {
        retList.forEach(function(item) {
          retList = retList.concat(getDependentList(item).slice(0));
        });
      }
    
      return retList;
    }
    
    function getRemoveList() {
      var id = document.getElementById("txtInput").value;
      var removeList = [];
      removeList.push(id);
      removeList = removeList.concat(getDependentList(id))
      console.log(removeList);
    }
    <input type="text" id="txtInput">
    <button onclick="getRemoveList()">get Lists</button>

    【讨论】:

      【解决方案8】:

      试试这个:

      HTML:

      <input type="text" id="Txt" />
      
      <button type="button" onclick="check();">
      Check
      </button>
      

      JS:

      data = [{
          "_id" : "1",
          "parentId" : "thisPostId",
          "topLevelId" : "1",
          "text" : "<p>comment</p>",
      },
      {
          "_id" : "2",
          "parentId" : "1",
          "topLevelId" : "1",
          "text" : "<p>reply to comment</p>",
      },
      {
          "_id" : "3",
          "parentId" : "2",
          "topLevelId" : "1",
          "text" : "<p>reply to reply to comment</p>",
      },
      {
          "_id" : "4",
          "parentId" : "3",
          "topLevelId" : "1",
          "text" : "<p>reply to reply to reply to comment</p>",
      }];
      
      
      function check() {
          getIds(document.getElementById("Txt").value);
      }
      
      function getIds(id) {
          var allow = false,
              result = [];
      
          for (var i = 0; i < data.length; i++) {
              if (data[i]._id == id) {
                  allow = true;
              }
              if (allow) {
                  result.push(data[i]._id)
              }
          }
      
          retrun result;
      }
      

      【讨论】:

      • 再说一次,如果 cmets 顺序不同,这将无法按预期工作。
      猜你喜欢
      • 1970-01-01
      • 2017-01-08
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      • 2015-04-19
      • 1970-01-01
      • 2016-06-05
      • 1970-01-01
      相关资源
      最近更新 更多