JSON对象中的JavaScript递归搜索

xqkwcwgp  于 2023-02-14  发布在  Java
关注(0)|答案(8)|浏览(149)

我尝试返回JSON对象结构中的特定节点,如下所示

{
    "id":"0",
    "children":[
        {
            "id":"1",
            "children":[...]
        },
        {
            "id":"2",
            "children":[...]
        }
    ]
}

所以它是一个树状的父子关系,每个 * 节点 * 都有一个唯一的ID,我正在尝试找到一个特定的 * 节点 *,就像这样

function findNode(id, currentNode) {

    if (id == currentNode.id) {
        return currentNode;
    } else {
        currentNode.children.forEach(function (currentChild) {            
            findNode(id, currentChild);
        });
    }
}

我执行了搜索,例如findNode("10", rootNode)。但是即使搜索找到了匹配,函数也总是返回undefined。我有一种不好的感觉,递归函数在找到匹配后没有停止,而是继续运行,最终返回undefined,因为在后面的递归执行中,它没有到达返回点,但是我不知道如何修复这个问题。
救命啊!

jchrr9hc

jchrr9hc1#

当递归搜索时,你必须通过返回来传递回结果,但是你不会返回findNode(id, currentChild)的结果。

function findNode(id, currentNode) {
    var i,
        currentChild,
        result;

    if (id == currentNode.id) {
        return currentNode;
    } else {

        // Use a for loop instead of forEach to avoid nested functions
        // Otherwise "return" will not work properly
        for (i = 0; i < currentNode.children.length; i += 1) {
            currentChild = currentNode.children[i];

            // Search in the current child
            result = findNode(id, currentChild);

            // Return the result if the node has been found
            if (result !== false) {
                return result;
            }
        }

        // The node has not been found and we have no more options
        return false;
    }
}
wqsoz72f

wqsoz72f2#

function findNode(id, currentNode) {

    if (id == currentNode.id) {
        return currentNode;
    } else {
        var result;
        currentNode.children.forEach(function(node){
            if(node.id == id){
                result = node;
                return;
            }
        });
        return (result ? result : "No Node Found");
    }
}
console.log(findNode("10", node));

这个方法将返回节点列表中的节点,但是这将遍历节点的所有子节点,因为我们不能成功地断开forEach流。一个更好的实现如下所示。

function findNode(id, currentNode) {

    if (id == currentNode.id) {
        return currentNode;
    } else {
        for(var index in currentNode.children){
            var node = currentNode.children[index];
            if(node.id == id)
                return node;
            findNode(id, node);
        }
        return "No Node Present";
    }
}
console.log(findNode("1", node));
kuarbcqp

kuarbcqp3#

我使用以下代码

var searchObject = function (object, matchCallback, currentPath, result, searched) {
    currentPath = currentPath || '';
    result = result || [];
    searched = searched || [];
    if (searched.indexOf(object) !== -1 && object === Object(object)) {
        return;
    }
    searched.push(object);
    if (matchCallback(object)) {
        result.push({path: currentPath, value: object});
    }
    try {
        if (object === Object(object)) {
            for (var property in object) {
                if (property.indexOf("$") !== 0) {
                    //if (Object.prototype.hasOwnProperty.call(object, property)) {
                        searchObject(object[property], matchCallback, currentPath + "." + property, result, searched);
                    //}
                }
            }
        }
    }
    catch (e) {
        console.log(object);
        throw e;
    }
    return result;
}

然后你可以写

searchObject(rootNode, function (value) { return value != null && value != undefined && value.id == '10'; });

现在,这个函数适用于循环引用,您可以通过修改matchCallback函数来匹配任何字段或字段组合。

yruzcnhs

yruzcnhs4#

既然这个老问题又被提了回来,这里有一个不同的方法。我们可以编写一个相当通用的searchTree函数,然后在findId函数中使用它。searchTree完成遍历对象的工作;它接受回调以及树;回调函数确定节点是否匹配。除了节点,回调函数还提供了两个函数,nextfound,我们调用这两个函数时不带任何参数,分别表示我们应该继续或者我们已经找到匹配。如果没有找到匹配,我们返回null
它看起来像这样:

const searchTree = (fn) => (obj) =>
  Array.isArray(obj)
    ? obj.length == 0
      ? null
      : searchTree (fn) (obj [0]) || searchTree (fn) (obj .slice (1))
    : fn (
      obj,
      () => searchTree (fn) (obj .children || []),
      () => obj
    )

const findId = (target, obj) => searchTree (
  (node, next, found) => node.id == target ? found () : next(),
) (tree)

const tree = {id: 1, name: 'foo', children: [
  {id: 2, name: 'bar', children: []}, 
  {id: 3, name: 'baz', children: [
    {id: 17, name: 'qux', children: []}, 
    {id: 42, name: 'corge', children: []},
    {id: 99, name: 'grault', children: []}
  ]}
]}

console .log (findId (42, tree))
console .log (findId (57, tree))

这段代码是特定于children属性下的数组中的子节点的结构,虽然我们可以根据需要使其更通用,但我发现这是一个需要支持的常见结构。
有一个很好的论点是,这将是更好地编写与相互递归。如果我们想,我们可以得到相同的API与此版本:

const searchArray = (fn) => ([x, ...xs]) =>
  x === undefined
    ? null
    : searchTree (fn) (x) || searchArray (fn) (xs)

const searchTree = (fn) => (obj) =>
  fn (
    obj,
    () => searchArray (fn) (obj .children || []),
    (x) => x
  )

这是同样的工作方式。但我发现代码更干净。不过,两者都应该完成这项工作。

bvpmtnay

bvpmtnay5#

我们使用object-scan来满足我们的数据处理需求。它在概念上非常简单,但允许使用很多很酷的东西。

// const objectScan = require('object-scan');

const findNode = (id, input) => objectScan(['**'], {
  abort: true,
  rtn: 'value',
  filterFn: ({ value }) => value.id === id
})(input);

const data = { id: '0', children: [{ id: '1', children: [ { id: '3', children: [] }, { id: '4', children: [] } ] }, { id: '2', children: [ { id: '5', children: [] }, { id: '6', children: [] } ] }] };

console.log(findNode('6', data));
// => { id: '6', children: [] }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>
bis0qfac

bis0qfac6#

我真的很喜欢树搜索!对于今天大多数复杂的结构化任务来说,树是一种非常常见的数据结构。所以我午餐也有类似的任务。我甚至做了一些深入的研究,但实际上没有发现任何新的东西!所以今天我为你准备的是“我是如何用现代JS语法实现它的”:

// helper
find_subid = (id, childArray) => {
    for( child of childArray ) {
        foundChild = find_id( i, child ); // not sub_id, but do a check (root/full search)!
        if( foundChild ) // 200 
            return foundChild;
    }
    return null; // 404
}

// actual search method
find_id = (id, parent) => (id == parent.id) : parent : find_subid(id, parent.childArray);
bwitn5fc

bwitn5fc7#

递归结构搜索、修改、键/值调整/替换。

用法示例:

const results = []; // to store the search results

mapNodesRecursively(obj, ({ v, key, obj, isCircular }) => {
    // do something cool with "v" (or key, or obj)
    // return nothing (undefined) to keep the original value

    // if we search:
    if (key === 'name' && v === 'Roman'){
        results.push(obj);
    }

    // more example flow:
    if (isCircular) {
        delete obj[key]; // optionally - we decide to remove circular links
    } else if (v === 'Russia') {
        return 'RU';
    } else if (key.toLocaleLowerCase() === 'foo') {
        return 'BAR';
    } else if (key === 'bad_key') {
        delete obj[key];
        obj['good_key'] = v;
    } else {
        return v; // or undefined, same effect
    }
});

提示和提示:

你可以把它作为一个搜索回调函数,不返回任何东西(不会影响任何东西),然后选择你需要的值到你的数组/集合/Map中。
注意回调在每个leaf/value/key(不仅仅是对象)上运行。
或者你可以使用回调函数来调整特定的值,甚至改变键。它还可以自动检测循环,并提供一个标志,让你决定如何处理它们。

密码

(uses ES6)
函数本身+一些示例演示数据
x一个一个一个一个x一个一个二个一个x一个一个三个一个

u59ebvdq

u59ebvdq8#

类似的问题已经回答过好几次了,但我只想添加一个包含嵌套数组的通用方法

const cars = [{
  id: 1,
  name: 'toyota',
  subs: [{
    id: 43,
    name: 'supra'
  }, {
    id: 44,
    name: 'prius'
  }]
}, {
  id: 2,
  name: 'Jeep',
  subs: [{
    id: 30,
    name: 'wranger'
  }, {
    id: 31,
    name: 'sahara'
  }]
}]

function searchObjectArray(arr, key, value) {
  let result = [];
  
  arr.forEach((obj) => {
    if (obj[key] === value) {
      result.push(obj);
    } else if (obj.subs) {
      result = result.concat(searchObjectArray(obj.subs, key, value));
    }
  });
  console.log(result)
  return result;
}

searchObjectArray(cars, 'id', '31') 

searchObjectArray(cars, 'name', 'Jeep')

我希望这能帮到一些人

相关问题