在嵌套对象中查找对象:超出了最大调用堆栈大小

时间:2019-02-28 05:33:50

标签: javascript arrays ecmascript-6

我有一个要迭代的对象数组,并返回嵌套对象,其中text属性等于给定的字符串参数。 例如,文本属性为“预订内部采访”的对象。要查找的对象可以位于层次结构的任何级别。

我正在尝试类似的操作,但这只允许我查看特定级别的内容:

var txt = "Book an Internal Interview";
var obj = items[0].items[0].items.find(o => o.text === txt); 

所以我尝试了一个递归解决方案:

var txt = "Book an Internal Interview";
var obj = customFilter(items, txt)

function customFilter(object, text) {
  if (object.hasOwnProperty('text') && object["text"] == text)
    return object;

  for (var i = 0; i < Object.keys(object).length; i++) {
    if (typeof object[Object.keys(object)[i]] == "object") {
      var o = customFilter(object[Object.keys(object)[i]],text);
      if (o != null)
        return o;
    }
  }

  return null;
} 

但是我得到一个错误:

  

超出了最大调用堆栈大小

这是我的对象在控制台中的样子:

0:
    action: ""
    id: "1"
    items: Array(18)
            0:
                action: "http://localhost:8085/secure/contacts/create.aspx?CandidateID=2256"
                id: "20"
                label: "Convert to Contact"
                leaf: true
                level: 1
                parent: {id: "1", parentid: "-1", action: "", typeAction: "", text: "Workflow", …}
                parentid: "1"
                text: "Convert to Contact"
                typeAction: "url"
                uid: "20"

            1:
                action: "deleteCandidate(2256);"
                id: "2"
                label: "Delete"
                leaf: true
                level: 1
                parent: {id: "1", parentid: "-1", action: "", typeAction: "", text: "Workflow", …}
                parentid: "1"
                text: "Delete"
                typeAction: "method"
                uid: "2"

            2: {id: "21", parentid: "1", action: "ResetModal(); $('#TemplateDialog').dialog('open'); candidateID = 2256", typeAction: "method", text: "Generate Document", …}
            3:
                action: ""
                id: "15"
                items: Array(2)
                    0:
                        action: "bookClientInterviewSingleCandidate('46','2256')"
                        id: "16"
                        label: "Book a Client Interview"
                        leaf: true
                        level: 2
                        parent: {id: "15", parentid: "1", action: "", typeAction: "", text: "Interviews", …}
                        parentid: "15"
                        text: "Book a Client Interview"
                        typeAction: "method"
                        uid: "16"

                    1:
                        action: "bookClientInterviewInternalSingleCandidate('0','2256')"
                        id: "17"
                        label: "Book an Internal Interview"
                        leaf: true
                        level: 2
                        parent: {id: "15", parentid: "1", action: "", typeAction: "", text: "Interviews", …}
                        parentid: "15"
                        text: "Book an Internal Interview"
                        typeAction: "method"
                        uid: "17"
                label: "Interviews"
                level: 1
                parent: {id: "1", parentid: "-1", action: "", typeAction: "", text: "Workflow", …}
                parentid: "1"
                text: "Interviews"
                typeAction: ""
                uid: "15"

            4:
                action: "DisplayLinkToJobForCandidate('modalDisplay', '2256', 'Abigail Hotmail')"
                id: "19"
                label: "Link To Job"
                leaf: true
                level: 1
                parent: {id: "1", parentid: "-1", action: "", typeAction: "", text: "Workflow", …}
                parentid: "1"
                text: "Link To Job"
                typeAction: "method"
                uid: "19"

            5: {id: "18", parentid: "1", action: "loadMessageCenterSingleCandidate(2256)", typeAction: "method", text: "Send Email", …}
            6: {id: "14", parentid: "1", action: "submitCVSingleCandidate('46','2256')", typeAction: "method", text: "Submit CV", …}
            .............

1 个答案:

答案 0 :(得分:2)

如注释中所述,您的输入对象具有循环引用,这意味着嵌套的子对象具有对其祖先之一的引用(可能是parent属性的情况)。因此,您的代码将无休止地遵循这样的“循环”路径。

您可以通过保留Set个访问过的对象来保护代码。

function customFilter(object, text, visited = new Set) {
  if (!object || visited.has(object)) return; // Do not go into loops and ignore nulls
  visited.add(object);
  if (object.hasOwnProperty('text') && object["text"] == text)
    return object;

  for (var i = 0; i < Object.keys(object).length; i++) {
    if (typeof object[Object.keys(object)[i]] == "object") {
      var o = customFilter(object[Object.keys(object)[i]], text, visited);
      if (o != null)
        return o;
    }
  }

  return null;
}

所以:

  • 该函数还有一个额外的Set参数。您不需要在初始调用中指定它,因为默认情况下它将是一个空Set,但是递归调用应传递第三个参数
  • 它检查对象是否已经在集合中。如果是这样,它将退出。
  • 否则,它将对象添加到集合中

Set将消耗一些内存(与原始输入大小成线性关系)。如果您知道是造成此类反向引用的parent属性,那么您也可以通过从循环中排除该属性来解决它。