program tip

중첩 된 개체에서 키로 찾기

radiobox 2020. 12. 12. 10:22
반응형

중첩 된 개체에서 키로 찾기


객체가 있다고 가정 해 보겠습니다.

[
    {
        'title': "some title"
        'channel_id':'123we'
        'options': [
                    {
                'channel_id':'abc'
                'image':'http://asdasd.com/all-inclusive-block-img.jpg'
                'title':'All-Inclusive'
                'options':[
                    {
                        'channel_id':'dsa2'
                        'title':'Some Recommends'
                        'options':[
                            {
                                'image':'http://www.asdasd.com'                                 'title':'Sandals'
                                'id':'1'
                                'content':{
                                     ...

ID가 1 인 하나의 객체를 찾고 싶습니다. 이와 같은 기능이 있습니까? Underscore의 _.filter방법을 사용할 수는 있지만 맨 위에서 시작하여 필터링해야합니다.


재귀는 당신의 친구입니다. 속성 배열을 설명하도록 함수를 업데이트했습니다.

function getObject(theObject) {
    var result = null;
    if(theObject instanceof Array) {
        for(var i = 0; i < theObject.length; i++) {
            result = getObject(theObject[i]);
            if (result) {
                break;
            }   
        }
    }
    else
    {
        for(var prop in theObject) {
            console.log(prop + ': ' + theObject[prop]);
            if(prop == 'id') {
                if(theObject[prop] == 1) {
                    return theObject;
                }
            }
            if(theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
                result = getObject(theObject[prop]);
                if (result) {
                    break;
                }
            } 
        }
    }
    return result;
}

업데이트 된 jsFiddle : http://jsfiddle.net/FM3qu/7/


객체가 검색되는 동안 id가 1 인 첫 번째 요소를 얻으려면 다음 함수를 사용할 수 있습니다.

function customFilter(object){
    if(object.hasOwnProperty('id') && object["id"] == 1)
        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]]);
            if(o != null)
                return o;
        }
    }

    return null;
}

id가 1 인 모든 요소를 ​​얻으려면 (id가 1 인 모든 요소가 결과에 저장됩니다) :

function customFilter(object, result){
    if(object.hasOwnProperty('id') && object.id == 1)
        result.push(object);

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

나를 위해 일한 것은 알고리즘 적으로 게으른 것이 아니라이 게으른 접근 방식이었습니다.)

if( JSON.stringify(object_name).indexOf("key_name") > -1 ) {
    console.log("Key Found");
}
else{
    console.log("Key not Found");
}

비슷한 기능에 대한 인터넷 검색을 통해이 페이지를 찾았습니다. Zach와 regularmike가 제공 한 작업을 기반으로 내 필요에 맞는 다른 버전을 만들었습니다.
BTW, 멋진 작품 Zah와 regularmike! 여기에 코드를 게시하겠습니다.

function findObjects(obj, targetProp, targetValue, finalResults) {

  function getObject(theObject) {
    let result = null;
    if (theObject instanceof Array) {
      for (let i = 0; i < theObject.length; i++) {
        getObject(theObject[i]);
      }
    }
    else {
      for (let prop in theObject) {
        if(theObject.hasOwnProperty(prop)){
          console.log(prop + ': ' + theObject[prop]);
          if (prop === targetProp) {
            console.log('--found id');
            if (theObject[prop] === targetValue) {
              console.log('----found porop', prop, ', ', theObject[prop]);
              finalResults.push(theObject);
            }
          }
          if (theObject[prop] instanceof Object || theObject[prop] instanceof Array){
            getObject(theObject[prop]);
          }
        }
      }
    }
  }

  getObject(obj);

}

그것이 무엇을하는 것은 어떤 객체의 내부 찾을 수 있습니다 obj에 속성 이름과 값의 매칭을 targetProp하고 targetValue그리고 그것을 밀어 버린다 finalResults배열입니다. 그리고 여기 jsfiddle이 있습니다 : https://jsfiddle.net/alexQch/5u6q2ybc/


이 목적으로 라이브러리를 만들었습니다 : https://github.com/dominik791/obj-traverse

다음 findFirst()과 같은 방법 을 사용할 수 있습니다 .

var foundObject = findFirst(rootObject, 'options', { 'id': '1' });

이제 foundObjectvariable은 찾고있는 객체에 대한 참조를 저장합니다.


키와 술어를 사용하여 @haitaka 답변 개선

function  deepSearch (object, key, predicate) {
    if (object.hasOwnProperty(key) && predicate(key, object[key]) === true) return object

    for (let i = 0; i < Object.keys(object).length; i++) {
      if (typeof object[Object.keys(object)[i]] === "object") {
        let o = deepSearch(object[Object.keys(object)[i]], key, predicate)
        if (o != null) return o
      }
    }
    return null
}

따라서 다음과 같이 호출 할 수 있습니다.

var result = deepSearch(myObject, 'id', (k, v) => v === 1);

또는

var result = deepSearch(myObject, 'title', (k, v) => v === 'Some Recommends');

다음은 jsFiddle입니다. http://jsfiddle.net/ktdx9es7


또 다른 (다소 어리석은) 옵션은의 자연적 재귀 적 특성을 이용하고 문자열 화 프로세스 동안 각 중첩 된 객체에서 실행 JSON.stringify되는 대체 함수전달하는 것입니다.

const input = [{
  'title': "some title",
  'channel_id': '123we',
  'options': [{
    'channel_id': 'abc',
    'image': 'http://asdasd.com/all-inclusive-block-img.jpg',
    'title': 'All-Inclusive',
    'options': [{
      'channel_id': 'dsa2',
      'title': 'Some Recommends',
      'options': [{
        'image': 'http://www.asdasd.com',
        'title': 'Sandals',
        'id': '1',
        'content': {}
      }]
    }]
  }]
}];

console.log(findNestedObj(input, 'id', '1'));

function findNestedObj(entireObj, keyToFind, valToFind) {
  let foundObj;
  JSON.stringify(input, (_, nestedValue) => {
    if (nestedValue && nestedValue[keyToFind] === valToFind) {
      foundObj = nestedValue;
    }
    return nestedValue;
  });
  return foundObj;
};


개체 내의 순환 참조를 고려하도록 답변이 개선되었습니다. 거기에 도달하는 데 걸린 경로도 표시됩니다.

이 예에서는 전역 개체 내 어딘가에있는 iframe을 검색하고 있습니다.

const objDone = []
var i = 2
function getObject(theObject, k) {
    if (i < 1 || objDone.indexOf(theObject) > -1) return
    objDone.push(theObject)
    var result = null;
    if(theObject instanceof Array) {
        for(var i = 0; i < theObject.length; i++) {
            result = getObject(theObject[i], i);
            if (result) {
                break;
            }   
        }
    }
    else
    {
        for(var prop in theObject) {
            if(prop == 'iframe' && theObject[prop]) {
                i--;
                console.log('iframe', theObject[prop])
                return theObject[prop]
            }
            if(theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
                result = getObject(theObject[prop], prop);
                if (result) {
                    break;
                }
            } 
        }
    }
    if (result) console.info(k)
    return result;
}

다음을 실행 : getObject(reader, 'reader')마지막에 다음 출력과 iframe 요소를 제공했습니다.

iframe // (The Dom Element)
_views
views
manager
rendition
book
reader

참고 : 경로는 역순입니다. reader.book.rendition.manager.views._views.iframe


I'd like to suggest an amendment to Zach/RegularMike's answer (but don't have the "reputation" to be able to comment!). I found there solution a very useful basis, but suffered in my application because if there are strings within arrays it would recursively call the function for every character in the string (which caused IE11 & Edge browsers to fail with "out of stack space" errors). My simple optimization was to add the same test used in the "object" clause recursive call to the one in the "array" clause:

if (arrayElem instanceof Object || arrayElem instanceof Array) {

Thus my full code (which is now looking for all instances of a particular key, so slightly different to the original requirement) is:

// Get all instances of specified property deep within supplied object
function getPropsInObject(theObject, targetProp) {
    var result = [];
    if (theObject instanceof Array) {
        for (var i = 0; i < theObject.length; i++) {
            var arrayElem = theObject[i];
            if (arrayElem instanceof Object || arrayElem instanceof Array) {
                result = result.concat(getPropsInObject(arrayElem, targetProp));
            }
        }
    } else {
        for (var prop in theObject) {
            var objProp = theObject[prop];
            if (prop == targetProp) {
                return theObject[prop];
            }
            if (objProp instanceof Object || objProp instanceof Array) {
                result = result.concat(getPropsInObject(objProp, targetProp));
            }
        }
    }
    return result;
}

I would try not to reinvent the wheel. We use object-scan for all our data processing needs. It's conceptually very simple, but allows for a lot of cool stuff. Here is how you would solve your specific question

Data Definition

const data = [{
  'title': "some title",
  'channel_id': '123we',
  'options': [{
    'channel_id': 'abc',
    'image': 'http://asdasd.com/all-inclusive-block-img.jpg',
    'title': 'All-Inclusive',
    'options': [{
      'channel_id': 'dsa2',
      'title': 'Some Recommends',
      'options': [{
        'image': 'http://www.asdasd.com',
        'title': 'Sandals',
        'id': '1',
        'content': {}
      }]
    }]
  }]
}];

Logic

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

const scanner = (input) => {
  let obj = null;
  objectScan(['**.id'], {
    filterFn: (key, value, { parents }) => {
      if (value === '1') {
        obj = parents[0];
      }
    },
    breakFn: () => obj !== null
  })(data);
  return obj;
};

const result = scanner(data);

Output

// result =>
{
  "image": "http://www.asdasd.com",
  "title": "Sandals",
  "id": "1",
  "content": {}
}

If you're already using Underscore, use _.find()

_.find(yourList, function (item) {
    return item.id === 1;
});

참고URL : https://stackoverflow.com/questions/15523514/find-by-key-deep-in-a-nested-object

반응형