programing

JSON 개체의 JavaScript 재귀 검색

fastcode 2023. 3. 26. 11:53
반응형

JSON 개체의 JavaScript 재귀 검색

다음과 같은 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왜냐하면 후자의 재귀 실행에서는 반환점에 도달하지 못하기 때문입니다만, 이것을 어떻게 수정해야 할지 모르겠습니다.

도와주세요!

재귀적으로 검색할 경우 결과를 반환하여 반환해야 합니다.당신은 그 결과를 반환하지 않을 것입니다.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;
    }
}
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));

다음을 사용합니다.

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기능.

이 오래된 문제가 다시 제기되었으니, 여기 다른 방법이 있습니다.우리는 꽤 일반적인 것을 쓸 수 있다.searchTree그 후 우리가 사용하는 기능findId기능. searchTree는 오브젝트를 통과하는 작업을 수행합니다.콜백은 트리뿐만 아니라 콜백도 받아들입니다.콜백은 노드가 일치하는지 여부를 판단합니다.노드뿐만 아니라 콜백에는 두 가지 기능이 제공됩니다.next그리고.found우리는 이것을 각각 계속 진행해야 한다는 신호나 일치하는 것을 발견했다는 신호 없이 호출합니다.일치하는 것이 발견되지 않으면 반환한다.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
  )

이것은 같은 방법으로 동작합니다.하지만 코드가 더 깨끗해요하지만 둘 중 어느 쪽이든 그 일을 할 수 있을 것이다.

데이터 처리 요구에는 객체 스캔을 사용합니다.개념적으로는 매우 심플하지만, 많은 멋진 것을 가능하게 합니다.여기 당신의 질문을 해결할 방법이 있습니다.

// 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>

면책사항:는 객체 스캔의 저자입니다.

나는 나무 찾기가 정말 좋았어!트리는 오늘날 대부분의 복잡한 구조화 작업에서 매우 일반적인 데이터 구조입니다.그래서 점심에도 비슷한 일을 했어요.나는 심지어 몇 가지 깊이 있는 조사도 해봤지만, 실제로 새로운 것을 발견하지 못했어!오늘은 "현대 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);

재귀 구조 검색, 수정, 키/값 조정/교체.

사용 예:

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
    }
});

힌트:

검색 콜백으로 사용할 수 있습니다.아무것도 반환하지 않고 어레이/세트/맵에 필요한 값을 선택할 수 있습니다.

콜백은 (오브젝트뿐만 아니라) 모든 리프/값/키에서 실행됩니다.

또는 콜백을 사용하여 특정 값을 조정하거나 키를 변경할 수도 있습니다.또, 순환 루프를 자동적으로 검출해, 처리 방법을 결정하는 플래그를 제공합니다.

코드

(ES6 사용)

기능 자체 + 데모 데이터 예시

function mapNodesRecursively(obj, mapCallback, { wereSet } = {}) {
    if (!wereSet) {
        wereSet = new Set();
    }

    if (obj && (obj === Object(obj) || Array.isArray(obj))) {
        wereSet.add(obj);

        for (let key in obj) {
            if (!obj.hasOwnProperty(key)){
                continue;
            }

            let v = obj[key];

            const isCircular = wereSet.has(v);

            const mapped = mapCallback({ v, key, obj, isCircular });
            if (typeof (mapped) !== 'undefined') {
                obj[key] = mapped;
                v = mapped;
            }

            if (!isCircular) {
                mapNodesRecursively(v, mapCallback, { wereSet });
            }
        }
    }

    return obj;
}

let obj = {
    team: [
        {
            name: 'Roman',
            country: 'Russia',
            bad_key: 123,
        },
        {
            name: 'Igor',
            country: 'Ukraine',
            FOO: 'what?',
        },
        {
            someBool: true,
            country: 'Russia',
        },
        123,
        [
            1,
            {
                country: 'Russia',
                just: 'a nested thing',
                a: [{
                    bad_key: [{
                        country: 'Russia',
                        foo: false,
                    }],
                }],
            },
        ],
    ],
};

// output the initial data
document.getElementById('jsInput').innerHTML = JSON.stringify(obj, null, 2);

// adding some circular link (to fix with our callback)
obj.team[1].loop = obj;

mapNodesRecursively(obj, ({ v, key, obj, isCircular }) => {
    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;
    }
});

// output the result - processed object
document.getElementById('jsOutput').innerHTML = JSON.stringify(obj, null, 2);
.col {
  display: inline-block;
  width: 40%;
}
<div>
  <h3>Recursive structure modification, keys/values adjustments/replacement</h3>
  <ol>
    <li>
      Replacing "Russia" values with "RU"
    </li>
    <li>
      Setting the value "BAR" for keys "FOO"
    </li>
    <li>
      Changing the key "bad_key" to "good_key"
    </li>
  </ol>
  <div class="col">
    <h4>BEFORE</h4>
    <pre id="jsInput"></pre>
  </div>
  <div class="col">
    <h4>AFTER</h4>
    <pre id="jsOutput"></pre>
  </div>
</div>

언급URL : https://stackoverflow.com/questions/22222599/javascript-recursive-search-in-json-object

반응형