【问题标题】:Inserting/navigating nested JavaScript object/array插入/导航嵌套的 JavaScript 对象/数组
【发布时间】:2017-02-06 08:23:23
【问题描述】:

我有这些数据:

const items = [
  {
    _id: 0,
    content: 'Item 1 something',
    note: 'Some note for item 1'
  },
  {
    _id: 5,
    content: 'Item 1.1 something',
    note: 'Some note for item 1.1'
  },
  {
    _id: 1,
    content: 'Item 2 something',
    note: 'Some note for item 2',
    subItems: [
      {
        _id: 2,
        parent_id: 1,
        content: 'Sub Item 1 something',
        subItems: [{
          _id: 3,
          parent_id: 2,
          content: 'Sub Sub Item 4'
        }]
      }
    ]
  }
];

使用 Javascript,我如何导航/插入到树中,前提是我在任何时候都拥有树中一项的 _id。

例如一些案例场景:

  • 我在 _id 3 并且想在 _id 3 中插入​​另一个兄弟
  • 我在 _id 2 并且想上升到 _id 1 - 我如何获得 _id 1?
  • 我在 _id 5,想去 _id 1

如何仅使用 _id 导航树?

【问题讨论】:

  • 我的错,更正了标题
  • 你的意思是你不知道对象的“深度/层次”,你想给它添加一个兄弟?
  • 确切地说,在任何时候,我都会在“项目”数组中的一个“项目”内。我希望能够获得它之前或之后的项目_id。
  • 这是可能的,但很棘手。您是否知道可能的重复,我将提出的答案将使用对象名称。所以如果你有 2 个名字,你可能会得到意想不到的结果,你可以接受吗?
  • 看看 Karol Selak 解决方案,这就是我的想法。您需要遍历您的对象并比较键名,并在找到时处理或返回结果。也看看我的答案stackoverflow.com/questions/41965517/…它处理深度和层次结构,可以给你一个线索:

标签: javascript


【解决方案1】:

您可以迭代数组并测试_id 属性是否具有所需的值。然后保存节点、父节点或数组的下一项。

为了获取父节点,实际的父节点被保存为闭包,如果找到想要的_id,则返回。

所有函数都将subItems 作为数组进行测试,如果是,则对subItems 执行迭代。

function getNode(array, id) {
    var node;
    array.some(function iter(a) {
        if (a._id === id) {
            node = a;
            return true;
        }
        return Array.isArray(a.subItems) && a.subItems.some(iter);
    });
    return node;
}

function getParent(array, id) {
    var parent ;
    array.some(function iter(p) {
        return function (a) {
            if (a._id === id) {
                parent = p;
                return true;
            }
            return Array.isArray(a.subItems) && a.subItems.some(iter(a));
        };
    }(undefined));
    return parent;
}

function getNextNode(array, id) {
    var node;
    array.some(function iter(a, i, aa) {
        if (a._id === id) {
            node = aa[i + 1];
            return true;
        }
        return Array.isArray(a.subItems) && a.subItems.some(iter);
    });
    return node;
}

var items = [{ _id: 0, content: 'Item 1 something', note: 'Some note for item 1' }, { _id: 5, content: 'Item 1.1 something', note: 'Some note for item 1.1' }, { _id: 1, content: 'Item 2 something', note: 'Some note for item 2', subItems: [{ _id: 2, parent_id: 1, content: 'Sub Item 1 something', subItems: [{ _id: 3, parent_id: 2, content: 'Sub Sub Item 4' }] }] }];

console.log(getNode(items, 3));
console.log(getParent(items, 2));
console.log(getNextNode(items, 5));
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

    【解决方案2】:

    使用 Javascript,我如何导航/插入到树中,前提是我在任何时候都拥有树中一项的 _id。

    您必须递归循环遍历树并跟踪您所在的位置和去过的位置。

    JavaScript 引用指向一个方向。除了对 _id 为 1 的对象的引用之外,什么都没有,你根本没有连接到它所在的数组。 (它甚至可以存在于多个数组或同一个数组的多个位置)。

    一般来说,你需要搜索树(递归是你的朋友)并跟踪你关心的成员的索引。

    一旦知道要处理的索引,就可以使用splice

    【讨论】:

      【解决方案3】:

      我已经为此类问题开发了解决方案。我称它为后视者。我的函数看起来:

      var backlooker = function(obj) {
        for (key in obj) {
          if (obj[key]._) {
            break;      
          }
          if (obj[key] instanceof Object) {
            obj[key]._ = obj;
            backlooker(obj[key])
          }
        }
        return obj;
      }
      

      你必须先改进你的对象:

      items = backlooker(items);
      

      现在你可以这样做了:

      a = items[2].subItems[0].subItems[0];
      c = a._._._._._._._;
      c == items; //true
      

      只有一个问题:如果您的对象中已经有名为 _ 的键,代码将无法正常工作(我认为这是非常罕见的情况,但有可能)。

      【讨论】:

        【解决方案4】:

        它不像“导航”那么简单。以下函数将遍历对象并提供您可以用来实现您想要的东西..例如名称、值、类型、子项数量和深度。

        还可以在下面查看您的案例的具体示例

        主要功能

        您只需像这样称呼它:loopThrough(items)并查看您的控制台了解详情。

        function loopThrough(obj, depth) {
            if (typeof(depth) === "undefined") {
                depth = 0; // depth 0 means the "root" of your object
            } else {
                depth++ // increase depth if exist... depth 1 means a property of an object on depth 0
            }
            for (keyName in obj) {
                let thisObj = obj[keyName] //value of this object
                let type = thisObj.constructor.name // type: Array, Object, String, Number or Function...
                if (type === "Object" || type === "Array") { // to check if this object "have children" to loop through
                    let childCount = type === "Object" ? Object.keys(thisObj).length : thisObj.length
                    console.group(depth + " (" + type + ") " + keyName + " : " + childCount) // starts a collapsable group called: depth, type and key
                    loopThrough(thisObj, depth) //loop through the child object
                    console.groupEnd() // closes the group
                } else { // doesn't have children (a String, Number or Function)
                    console.log(depth + " (" + type + ") " + keyName + " : " + thisObj) // types: depth, type key and value
                }
            }
        }
        

        示例

        这是一个针对 _id:3 的示例,在此示例中,我向想要的键添加了一个兄弟。

        loopThrough(items, "_id", 3)
        
        function loopThrough(obj, wantedKey = "", wantedValue = "", depth) {
            if (typeof(depth) === "undefined") {
                depth = 0;
            } else {
                depth++
            }
            for (keyName in obj) {
                let thisObj = obj[keyName]
                let type = thisObj.constructor.name
                if (type === "Object" || type === "Array") {
                    let childCount = type === "Object" ? Object.keys(thisObj).length : thisObj.length
                    loopThrough(thisObj, wantedKey, wantedValue, depth)
                }
                if (keyName === wantedKey && thisObj === wantedValue){
                  siblings = Object.keys(obj)
                  console.log('%c Hello!, I am ' + wantedKey +":"+ wantedValue, 'color: green');
                  console.log('%c I have '+ siblings.length + " siblings: " + siblings.toString(), 'color: green');
                  console.log("%c adding a new sibling...", 'color: grey')
                  obj["new_sibling"] = "new_sibling_value" // add a sibling to _id 3
                  siblings = Object.keys(obj)
                  console.log('%c now I have '+ siblings.length + " siblings: " + siblings.toString(), 'color: green');
                  console.log('%c I am at depth ' + depth, 'color: blue');
                  console.log('%c it should be simple to find a way to get my parent _id at depth ' + (depth - 1)  , 'color: blue');ParentID
                  console.log(JSON.stringify(items, null, 4));
                }
            }
        }
        
        • 对于您的第二个请求,您必须调整函数以存储所需密钥的深度,并通过调用函数或创建另一个函数在 depth - 1 查找其父 _id

        • 对于第三个请求,您可以count++ 密钥,一旦找到wantedKey,您将存储计数并再次循环并查找count - 1 aka 上一个兄弟或count + 1 aka 下一个兄弟

        如您所见,这不是一项简单的任务,但只要有一些创造力,就完全可以做到,祝您好运。

        【讨论】:

          猜你喜欢
          • 2020-03-25
          • 1970-01-01
          • 1970-01-01
          • 2020-02-03
          • 1970-01-01
          • 1970-01-01
          • 2016-12-23
          • 2011-12-02
          • 1970-01-01
          相关资源
          最近更新 更多