Programing

중첩 된 JavaScript 개체 키의 존재 여부 테스트

crosscheck 2020. 10. 3. 09:58
반응형

중첩 된 JavaScript 개체 키의 존재 여부 테스트


객체에 대한 참조가있는 경우 :

var test = {};

잠재적으로 (즉각적인 것은 아님) 다음과 같은 중첩 된 개체가 있습니다.

{level1: {level2: {level3: "level3"}}};

깊이 중첩 된 객체에서 속성의 존재를 확인하는 가장 좋은 방법은 무엇입니까?

alert(test.level1);를 산출 undefined하지만 alert(test.level1.level2.level3);실패합니다.

나는 현재 다음과 같은 일을하고 있습니다.

if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
    alert(test.level1.level2.level3);
}

하지만 더 나은 방법이 있는지 궁금합니다.


TypeError멤버 중 하나가 null또는 undefined이고 멤버 에 액세스하려고하면 예외가 발생 하기 때문에를 원하지 않으면 단계별로 수행해야합니다 .

단순히 catch예외를 지정하거나 다음과 같이 여러 수준의 존재를 테스트하는 함수를 만들 수 있습니다 .

function checkNested(obj /*, level1, level2, ... levelN*/) {
  var args = Array.prototype.slice.call(arguments, 1);

  for (var i = 0; i < args.length; i++) {
    if (!obj || !obj.hasOwnProperty(args[i])) {
      return false;
    }
    obj = obj[args[i]];
  }
  return true;
}

var test = {level1:{level2:{level3:'level3'}} };

checkNested(test, 'level1', 'level2', 'level3'); // true
checkNested(test, 'level1', 'level2', 'foo'); // false

2019-05-16 업데이트 :

다음은 ES6 기능과 재귀를 사용하는 더 짧은 버전입니다 ( 적절한 마무리 호출 형식 이기도 함 ).

function checkNested(obj, level,  ...rest) {
  if (obj === undefined) return false
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true
  return checkNested(obj[level], ...rest)
}

다음은 Oliver Steele에서 선택한 패턴입니다 .

var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );

사실 전체 기사는 자바 스크립트에서 어떻게 할 수 있는지에 대한 토론입니다. 그는 위의 구문 (익숙해지면 읽기 어렵지 않음)을 관용구로 사용하기로 결정했습니다.


최신 정보

lodash _.get 필요한 모든 중첩 속성을 추가 한 것 같습니다.

_.get(countries, 'greece.sparta.playwright')

https://lodash.com/docs#get


이전 답변

lodash 사용자는 이 문제를 완화 하는 몇 가지 방법 이있는 lodash.contrib즐길 수 있습니다 .

getPath

서명: _.getPath(obj:Object, ks:String|Array)

주어진 키로 설명 된 경로를 기반으로 중첩 된 개체의 모든 깊이에서 값을 가져옵니다. 키는 배열 또는 점으로 구분 된 문자열로 제공 될 수 있습니다. undefined경로에 도달 할 수없는 경우 반환 합니다.

var countries = {
        greece: {
            athens: {
                playwright:  "Sophocles"
            }
        }
    }
};

_.getPath(countries, "greece.athens.playwright");
// => "Sophocles"

_.getPath(countries, "greece.sparta.playwright");
// => undefined

_.getPath(countries, ["greece", "athens", "playwright"]);
// => "Sophocles"

_.getPath(countries, ["greece", "sparta", "playwright"]);
// => undefined

내가 수행 한 성능 테스트를 (당신에게 감사 cdMinix을 아래에 나열된 결과이 질문에 제안 된 아이디어에 대한 lodash를 추가).

면책 조항 # 1 문자열을 참조로 바꾸는 것은 불필요한 메타 프로그래밍이며 아마도 피하는 것이 가장 좋습니다. 처음부터 참조를 잃어 버리지 마십시오. 이 답변에서 유사한 질문에 대해 자세히 읽어보십시오 .

면책 조항 # 2 여기서는 밀리 초당 수백만 개의 작업에 대해 이야기하고 있습니다. 이들 중 어느 것도 대부분의 사용 사례에서 큰 차이를 만들 가능성은 거의 없습니다. 각각의 한계를 아는 것이 가장 합리적인 것을 선택하십시오. 나를 위해 나는 reduce편리하지 않은 것과 같은 것을 가지고 갈 것입니다 .

Object Wrap (Oliver Steele 제작) – 34 % – 가장 빠름

var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;

원래 솔루션 (문제에서 제 안됨) – 45 %

var r1 = test.level1 && test.level1.level2 && test.level1.level2.level3;
var r2 = test.level1 && test.level1.level2 && test.level1.level2.foo;

checkNested – 50 %

function checkNested(obj) {
  for (var i = 1; i < arguments.length; i++) {
    if (!obj.hasOwnProperty(arguments[i])) {
      return false;
    }
    obj = obj[arguments[i]];
  }
  return true;
}

get_if_exist – 52 %

function get_if_exist(str) {
    try { return eval(str) }
    catch(e) { return undefined }
}

validChain – 54 %

function validChain( object, ...keys ) {
    return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}

objHasKeys – 63 %

function objHasKeys(obj, keys) {
  var next = keys.shift();
  return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}

nestedPropertyExists – 69 %

function nestedPropertyExists(obj, props) {
    var prop = props.shift();
    return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}

_.get – 72 %

가장 깊은 – 86 %

function deeptest(target, s){
    s= s.split('.')
    var obj= target[s.shift()];
    while(obj && s.length) obj= obj[s.shift()];
    return obj;
}

슬픈 광대 – 100 % – 가장 느림

var o = function(obj) { return obj || {} };

var r1 = o(o(o(o(test).level1).level2).level3);
var r2 = o(o(o(o(test).level1).level2).foo);

문자열처럼 이름을 처리하면 모든 깊이에서 객체 속성을 읽을 수 있습니다 't.level1.level2.level3'..

window.t={level1:{level2:{level3: 'level3'}}};

function deeptest(s){
    s= s.split('.')
    var obj= window[s.shift()];
    while(obj && s.length) obj= obj[s.shift()];
    return obj;
}

alert(deeptest('t.level1.level2.level3') || 'Undefined');

undefined세그먼트 중 하나라도이면 반환 됩니다 undefined.


var a;

a = {
    b: {
        c: 'd'
    }
};

function isset (fn) {
    var value;
    try {
        value = fn();
    } catch (e) {
        value = undefined;
    } finally {
        return value !== undefined;
    }
};

// ES5
console.log(
    isset(function () { return a.b.c; }),
    isset(function () { return a.b.c.d.e.f; })
);

당신이 ES6 환경에서 코딩 (또는 사용하는 경우 6to5를 ) 다음은 이용 취할 수 화살표 기능 구문 :

// ES6 using the arrow function
console.log(
    isset(() => a.b.c),
    isset(() => a.b.c.d.e.f)
);

성능과 관련하여 try..catch속성이 설정된 경우 블록 사용에 대한 성능 저하는 없습니다 . 속성이 설정되지 않으면 성능에 영향을 미칩니다.

다음을 사용하는 것이 좋습니다 _.has.

var object = { 'a': { 'b': { 'c': 3 } } };

_.has(object, 'a');
// → true

_.has(object, 'a.b.c');
// → true

_.has(object, ['a', 'b', 'c']);
// → true

어때

try {
   alert(test.level1.level2.level3)
} catch(e) {
 ...whatever

}

ES6 답변, 철저한 테스트 :)

const propExists = (obj, path) => {
    return !!path.split('.').reduce((obj, prop) => {
        return obj && obj[prop] ? obj[prop] : undefined;
    }, obj)
}

전체 테스트 범위가 포함 된 Codepen 참조


tc39 선택적 체인 제안을 babel 7과 함께 사용할 수도 있습니다 -tc39-proposal-optional-chaining

코드는 다음과 같습니다.

  const test = test?.level1?.level2?.level3;
  if (test) alert(test);

재귀 적 접근을 시도했습니다.

function objHasKeys(obj, keys) {
  var next = keys.shift();
  return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}

! keys.length ||재귀 밖으로 걷어차는 테스트에 남아있는 키를 사용하여 기능을 실행하지 않도록. 테스트 :

obj = {
  path: {
    to: {
      the: {
        goodKey: "hello"
      }
    }
  }
}

console.log(objHasKeys(obj, ['path', 'to', 'the', 'goodKey'])); // true
console.log(objHasKeys(obj, ['path', 'to', 'the', 'badKey']));  // undefined

알 수없는 키 / 값이있는 여러 개체의 친숙한 HTML보기를 인쇄하는 데 사용하고 있습니다. 예 :

var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
             ? myObj.MachineInfo.BiosInfo.Name
             : 'unknown';

다음 스크립트가 더 읽기 쉬운 표현을 제공한다고 생각합니다.

함수 선언 :

var o = function(obj) { return obj || {};};

다음과 같이 사용하십시오.

if (o(o(o(o(test).level1).level2).level3)
{

}

나는 기호 o (를 사용하기 때문에 "슬픈 광대 기법"이라고 부른다.


편집하다:

여기 TypeScript 용 버전이 있습니다.

컴파일 타임에 유형 검사를 제공합니다 (Visual Studio와 같은 도구를 사용하는 경우 intellisense도 포함).

export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
    if (typeof someObject === 'undefined' || someObject === null)
        return defaultValue;
    else
        return someObject;
}

사용법은 동일합니다.

o(o(o(o(test).level1).level2).level3

그러나 이번에는 intellisense가 작동합니다!

또한 기본값을 설정할 수 있습니다.

o(o(o(o(o(test).level1).level2).level3, "none")

한 가지 간단한 방법은 다음과 같습니다.

try {
    alert(test.level1.level2.level3);
} catch(e) {
    alert("undefined");    // this is optional to put any output here
}

try/catchtest, test.level1, test.level1.level2와 같은 상위 레벨 객체가 정의되지 않은 경우를 포착합니다.


프록시를 사용하는 사람의 예를 보지 못했습니다.

그래서 나는 내 자신을 생각해 냈습니다. 이것의 가장 좋은 점은 문자열을 보간 할 필요가 없다는 것입니다. 실제로 체인 가능한 객체 함수를 반환하고 이로 몇 가지 마법 같은 일을 할 수 있습니다. 함수를 호출하고 배열 인덱스를 가져 와서 깊은 객체를 확인할 수도 있습니다.

function resolve(target) {
  var noop = () => {} // We us a noop function so we can call methods also
  return new Proxy(noop, {
    get(noop, key) {
      // return end result if key is _result
      return key === '_result' 
        ? target 
        : resolve( // resolve with target value or undefined
            target === undefined ? undefined : target[key]
          )
    },

    // if we want to test a function then we can do so alos thanks to using noop
    // instead of using target in our proxy
    apply(noop, that, args) {
      return resolve(typeof target === 'function' ? target.apply(that, args) : undefined)
    },
  })
}

// some modified examples from the accepted answer
var test = {level1: {level2:() => ({level3:'level3'})}}
var test1 = {key1: {key2: ['item0']}}

// You need to get _result in the end to get the final result

console.log(resolve(test).level1.level2().level3._result)
console.log(resolve(test).level1.level2().level3.level4.level5._result)
console.log(resolve(test1).key1.key2[0]._result)
console.log(resolve(test1)[0].key._result) // don't exist

위의 코드는 동기식으로 잘 작동합니다. 하지만이 ajax 호출과 같이 비동기적인 것을 어떻게 테스트할까요? 어떻게 테스트합니까? 500 http 오류를 반환 할 때 응답이 json이 아니면 어떻게합니까?

window.fetch('https://httpbin.org/get')
.then(function(response) {
  return response.json()
})
.then(function(json) {
  console.log(json.headers['User-Agent'])
})

async / await를 사용하여 일부 콜백을 제거 할 수 있는지 확인하십시오. 하지만 더 마술처럼 할 수 있다면 어떨까요? 다음과 같이 보입니다.

fetch('https://httpbin.org/get').json().headers['User-Agent']

모든 promise와 .thenchain이 어디에 있는지 궁금 할 것입니다 ... 이것은 여러분이 아는 모든 것을 차단할 수 있습니다 ...하지만 promise와 동일한 Proxy 기술을 사용하면 실제로 단일 함수를 작성하지 않고도 존재에 대해 깊이 중첩 된 복잡한 경로를 테스트 할 수 있습니다.

function resolve(target) { 
  return new Proxy(() => {}, {
    get(noop, key) {
      return key === 'then' ? target.then.bind(target) : resolve(
        Promise.resolve(target).then(target => {
          if (typeof target[key] === 'function') return target[key].bind(target)
          return target[key]
        })
      )
    },

    apply(noop, that, args) {
      return resolve(target.then(result => {
        return result.apply(that, args)
      }))
    },
  })
}

// this feels very much synchronous but are still non blocking :)
resolve(window) // this will chain a noop function until you call then()
  .fetch('https://httpbin.org/get')
  .json()
  .headers['User-Agent']
  .then(console.log, console.warn) // you get a warning if it doesn't exist
  
// You could use this method also for the first test object
// also, but it would have to call .then() in the end



// Another example
resolve(window)
  .fetch('https://httpbin.org/get?items=4&items=2')
  .json()
  .args
  .items
  // nice that you can map an array item without even having it ready
  .map(n => ~~n * 4) 
  .then(console.log, console.warn) // you get a warning if it doesn't exist


이 답변을 바탕으로 ES2015문제를 해결할 수있는이 일반적인 기능을 생각해 냈습니다.

function validChain( object, ...keys ) {
    return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}

var test = {
  first: {
    second: {
        third: "This is not the key your are looking for"
    }
  }
}

if ( validChain( test, "first", "second", "third" ) ) {
    console.log( test.first.second.third );
}

중첩 된 개체 속성을 안전하게 가져 오는 작은 함수를 만들었습니다.

function getValue(object, path, fallback, fallbackOnFalsy) {
    if (!object || !path) {
        return fallback;
    }

    // Reduces object properties to the deepest property in the path argument.
    return path.split('.').reduce((object, property) => {
       if (object && typeof object !== 'string' && object.hasOwnProperty(property)) {
            // The property is found but it may be falsy.
            // If fallback is active for falsy values, the fallback is returned, otherwise the property value.
            return !object[property] && fallbackOnFalsy ? fallback : object[property];
        } else {
            // Returns the fallback if current chain link does not exist or it does not contain the property.
            return fallback;
        }
    }, object);
}

또는 더 간단하지만 약간 읽을 수없는 버전 :

function getValue(o, path, fb, fbFalsy) {
   if(!o || !path) return fb;
   return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? !o[p] && fbFalsy ? fb : o[p] : fb, o);
}

또는 더 짧지 만 거짓 플래그에 대한 폴 백이 없습니다.

function getValue(o, path, fb) {
   if(!o || !path) return fb;
   return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? o[p] : fb, o);
}

나는 다음과 함께 테스트했습니다.

const obj = {
    c: {
        a: 2,
        b: {
            c: [1, 2, 3, {a: 15, b: 10}, 15]
        },
        c: undefined,
        d: null
    },
    d: ''
}

다음은 몇 가지 테스트입니다.

// null
console.log(getValue(obj, 'c.d', 'fallback'));

// array
console.log(getValue(obj, 'c.b.c', 'fallback'));

// array index 2
console.log(getValue(obj, 'c.b.c.2', 'fallback'));

// no index => fallback
console.log(getValue(obj, 'c.b.c.10', 'fallback'));

문서 및 테스트가 포함 된 모든 코드를 보려면 내 github 요점을 확인할 수 있습니다. https://gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues-js


@CMS의 탁월한 답변의 더 짧은 ES5 버전 :

// Check the obj has the keys in the order mentioned. Used for checking JSON results.  
var checkObjHasKeys = function(obj, keys) {
  var success = true;
  keys.forEach( function(key) {
    if ( ! obj.hasOwnProperty(key)) {
      success = false;
    }
    obj = obj[key];
  })
  return success;
}

유사한 테스트를 통해 :

var test = { level1:{level2:{level3:'result'}}};
utils.checkObjHasKeys(test, ['level1', 'level2', 'level3']); // true
utils.checkObjHasKeys(test, ['level1', 'level2', 'foo']); // false

CMS가 제공하는 대답은 null 검사에 대한 다음 수정으로도 잘 작동합니다.

function checkNested(obj /*, level1, level2, ... levelN*/) 
      {
             var args = Array.prototype.slice.call(arguments),
             obj = args.shift();

            for (var i = 0; i < args.length; i++) 
            {
                if (obj == null || !obj.hasOwnProperty(args[i]) ) 
                {
                    return false;
                }
                obj = obj[args[i]];
            }
            return true;
    }

이 답변 부터 다음 옵션이 정교 해졌습니다 . 두 가지 모두에 대해 동일한 트리 :

var o = { a: { b: { c: 1 } } };

정의되지 않은 경우 검색 중지

var u = undefined;
o.a ? o.a.b ? o.a.b.c : u : u // 1
o.x ? o.x.y ? o.x.y.z : u : u // undefined
(o = o.a) ? (o = o.b) ? o.c : u : u // 1

각 레벨을 하나씩 확인하십시오.

var $ = function (empty) {
    return function (node) {
        return node || empty;
    };
}({});

$($(o.a).b).c // 1
$($(o.x).y).z // undefined

이 질문이 오래되었다는 것을 알고 있지만 모든 객체에 이것을 추가하여 확장을 제공하고 싶었습니다. 나는 사람들이 확장 된 객체 기능을 위해 Object 프로토 타입을 사용하는 것에 눈살을 찌푸리는 경향이 있다는 것을 알고 있지만,이 작업보다 쉬운 것은 없습니다. 또한 이제 Object.defineProperty 메서드를 사용할 수 있습니다.

Object.defineProperty( Object.prototype, "has", { value: function( needle ) {
    var obj = this;
    var needles = needle.split( "." );
    for( var i = 0; i<needles.length; i++ ) {
        if( !obj.hasOwnProperty(needles[i])) {
            return false;
        }
        obj = obj[needles[i]];
    }
    return true;
}});

이제 모든 객체의 속성을 테스트하려면 다음을 수행하면됩니다.

if( obj.has("some.deep.nested.object.somewhere") )

여기 에 테스트 할 jsfiddle 이 있으며, 특히 속성이 열거 가능해 지기 때문에 Object.prototype을 직접 수정하면 중단되는 일부 jQuery가 포함되어 있습니다. 이것은 타사 라이브러리에서 잘 작동합니다.


나는 이것이 약간의 개선이라고 생각합니다 (1 라이너가 됨).

   alert( test.level1 && test.level1.level2 && test.level1.level2.level3 )

이것은 && 연산자가 평가 한 최종 피연산자를 반환하고 단락되기 때문에 작동합니다.


속성이있는 경우 반환 할 값을 찾고 있었으므로 위의 CMS에서 답변을 수정했습니다. 내가 생각 해낸 것은 다음과 같습니다.

function getNestedProperty(obj, key) {
  // Get property array from key string
  var properties = key.split(".");

  // Iterate through properties, returning undefined if object is null or property doesn't exist
  for (var i = 0; i < properties.length; i++) {
    if (!obj || !obj.hasOwnProperty(properties[i])) {
      return;
    }
    obj = obj[properties[i]];
  }

  // Nested property found, so return the value
  return obj;
}


Usage:

getNestedProperty(test, "level1.level2.level3") // "level3"
getNestedProperty(test, "level1.level2.foo") // undefined


이것은 모든 객체와 배열에서 작동합니다. :)

전의:

if( obj._has( "something.['deep']['under'][1][0].item" ) ) {
    //do something
}

이것은 Brian의 대답의 향상된 버전입니다.

_has 를 기존 has 속성 (예 : maps)과 충돌 할 수 있으므로 속성 이름으로 사용 했습니다.

Object.defineProperty( Object.prototype, "_has", { value: function( needle ) {
var obj = this;
var needles = needle.split( "." );
var needles_full=[];
var needles_square;
for( var i = 0; i<needles.length; i++ ) {
    needles_square = needles[i].split( "[" );
    if(needles_square.length>1){
        for( var j = 0; j<needles_square.length; j++ ) {
            if(needles_square[j].length){
                needles_full.push(needles_square[j]);
            }
        }
    }else{
        needles_full.push(needles[i]);
    }
}
for( var i = 0; i<needles_full.length; i++ ) {
    var res = needles_full[i].match(/^((\d+)|"(.+)"|'(.+)')\]$/);
    if (res != null) {
        for (var j = 0; j < res.length; j++) {
            if (res[j] != undefined) {
                needles_full[i] = res[j];
            }
        }
    }

    if( typeof obj[needles_full[i]]=='undefined') {
        return false;
    }
    obj = obj[needles_full[i]];
}
return true;
}});

여기 바이올린이 있습니다


여기에 내 의견이 있습니다. 이러한 솔루션의 대부분은 다음과 같이 중첩 배열의 경우를 무시합니다.

    obj = {
        "l1":"something",
        "l2":[{k:0},{k:1}],
        "l3":{
            "subL":"hello"
        }
    }

확인하고 싶을 수 있습니다 obj.l2[0].k

아래 기능으로 할 수 있습니다 deeptest('l2[0].k',obj)

이 함수는 객체가 존재하면 true를 반환하고 그렇지 않으면 false를 반환합니다.

function deeptest(keyPath, testObj) {
    var obj;

    keyPath = keyPath.split('.')
    var cKey = keyPath.shift();

    function get(pObj, pKey) {
        var bracketStart, bracketEnd, o;

        bracketStart = pKey.indexOf("[");
        if (bracketStart > -1) { //check for nested arrays
            bracketEnd = pKey.indexOf("]");
            var arrIndex = pKey.substr(bracketStart + 1, bracketEnd - bracketStart - 1);
            pKey = pKey.substr(0, bracketStart);
			var n = pObj[pKey];
            o = n? n[arrIndex] : undefined;

        } else {
            o = pObj[pKey];
        }
        return o;
    }

    obj = get(testObj, cKey);
    while (obj && keyPath.length) {
        obj = get(obj, keyPath.shift());
    }
    return typeof(obj) !== 'undefined';
}

var obj = {
    "l1":"level1",
    "arr1":[
        {"k":0},
        {"k":1},
        {"k":2}
    ],
    "sub": {
       	"a":"letter A",
        "b":"letter B"
    }
};
console.log("l1: " + deeptest("l1",obj));
console.log("arr1[0]: " + deeptest("arr1[0]",obj));
console.log("arr1[1].k: " + deeptest("arr1[1].k",obj));
console.log("arr1[1].j: " + deeptest("arr1[1].j",obj));
console.log("arr1[3]: " + deeptest("arr1[3]",obj));
console.log("arr2: " + deeptest("arr2",obj));


이제을 사용 reduce하여 중첩 된 키를 반복 할 수도 있습니다 .

// @params o<object>
// @params path<string> expects 'obj.prop1.prop2.prop3'
// returns: obj[path] value or 'false' if prop doesn't exist

const objPropIfExists = o => path => {
  const levels = path.split('.');
  const res = (levels.length > 0) 
    ? levels.reduce((a, c) => a[c] || 0, o)
    : o[path];
  return (!!res) ? res : false
}

const obj = {
  name: 'Name',
  sys: { country: 'AU' },
  main: { temp: '34', temp_min: '13' },
  visibility: '35%'
}

const exists = objPropIfExists(obj)('main.temp')
const doesntExist = objPropIfExists(obj)('main.temp.foo.bar.baz')

console.log(exists, doesntExist)


재귀 함수를 사용하여이를 수행 할 수 있습니다. 중첩 된 객체 키 이름을 모두 알지 못하는 경우에도 작동합니다.

function FetchKeys(obj) {
    let objKeys = [];
    let keyValues = Object.entries(obj);
    for (let i in keyValues) {
        objKeys.push(keyValues[i][0]);
        if (typeof keyValues[i][1] == "object") {
            var keys = FetchKeys(keyValues[i][1])
            objKeys = objKeys.concat(keys);
        }
    }
    return objKeys;
}

let test = { level1: { level2: { level3: "level3" } } };
let keyToCheck = "level2";
let keys = FetchKeys(test); //Will return an array of Keys

if (keys.indexOf(keyToCheck) != -1) {
    //Key Exists logic;
}
else {
    //Key Not Found logic;
}

안전한 방식으로이 작업을 수행하는 codeabode (safeRead)에 함수가 있습니다 . 즉

safeRead(test, 'level1', 'level2', 'level3');

속성이 null이거나 정의되지 않은 경우 빈 문자열이 반환됩니다.


이전 주석을 기반으로 하여 기본 개체를 정의 할 수없는 또 다른 버전이 있습니다.

// Supposing that our property is at first.second.third.property:
var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;

원하는 경로를 취하고 좋고 나쁜 콜백 함수가있는 자체 함수를 작성했습니다.

function checkForPathInObject(object, path, callbackGood, callbackBad){
    var pathParts = path.split(".");
    var currentObjectPath = object;

    // Test every step to see if it exists in object
    for(var i=0; i<(pathParts.length); i++){
        var currentPathPart = pathParts[i];
        if(!currentObjectPath.hasOwnProperty(pathParts[i])){
            if(callbackBad){
                callbackBad();
            }
            return false;
        } else {
            currentObjectPath = currentObjectPath[pathParts[i]];
        }
    }

    // call full path in callback
    callbackGood();
}

용법:

var testObject = {
    level1:{
        level2:{
            level3:{
            }
        }
    }
};


checkForPathInObject(testObject, "level1.level2.level3", function(){alert("good!")}, function(){alert("bad!")}); // good

checkForPathInObject(testObject, "level1.level2.level3.levelNotThere", function(){alert("good!")}, function(){alert("bad!")}); //bad

//Just in case is not supported or not included by your framework
//***************************************************
Array.prototype.some = function(fn, thisObj) {
  var scope = thisObj || window;
  for ( var i=0, j=this.length; i < j; ++i ) {
    if ( fn.call(scope, this[i], i, this) ) {
      return true;
    }
  }
  return false;
};
//****************************************************

function isSet (object, string) {
  if (!object) return false;
  var childs = string.split('.');
  if (childs.length > 0 ) {
    return !childs.some(function (item) {
      if (item in object) {
        object = object[item]; 
        return false;
      } else return true;
    });
  } else if (string in object) { 
    return true;
  } else return false;
}

var object = {
  data: {
    item: {
      sub_item: {
        bla: {
          here : {
            iam: true
          }
        }
      }
    }
  }
};

console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'x')); // false
console.log(isSet(object,'data.sub_item')); // false
console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'data.item.sub_item.bla.here.iam')); // true

나는 같은 문제를 겪고 있었고 내 자신의 해결책을 찾을 수 있는지보고 싶었습니다. 확인하려는 경로를 문자열로 허용합니다.

function checkPathForTruthy(obj, path) {
  if (/\[[a-zA-Z_]/.test(path)) {
    console.log("Cannot resolve variables in property accessors");
    return false;
  }

  path = path.replace(/\[/g, ".");
  path = path.replace(/]|'|"/g, "");
  path = path.split(".");

  var steps = 0;
  var lastRef = obj;
  var exists = path.every(key => {
    var currentItem = lastRef[path[steps]];
    if (currentItem) {
      lastRef = currentItem;
      steps++;
      return true;
    } else {
      return false;
    }
  });

  return exists;
}

다음은 몇 가지 로깅 및 테스트 사례가있는 스 니펫입니다.

console.clear();
var testCases = [
  ["data.Messages[0].Code", true],
  ["data.Messages[1].Code", true],
  ["data.Messages[0]['Code']", true],
  ['data.Messages[0]["Code"]', true],
  ["data[Messages][0]['Code']", false],
  ["data['Messages'][0]['Code']", true]
];
var path = "data.Messages[0].Code";
var obj = {
  data: {
    Messages: [{
      Code: "0"
    }, {
      Code: "1"
    }]
  }
}

function checkPathForTruthy(obj, path) {
  if (/\[[a-zA-Z_]/.test(path)) {
    console.log("Cannot resolve variables in property accessors");
    return false;
  }

  path = path.replace(/\[/g, ".");
  path = path.replace(/]|'|"/g, "");
  path = path.split(".");

  var steps = 0;
  var lastRef = obj;
  var logOutput = [];
  var exists = path.every(key => {
    var currentItem = lastRef[path[steps]];
    if (currentItem) {
      logOutput.push(currentItem);
      lastRef = currentItem;
      steps++;
      return true;
    } else {
      return false;
    }
  });
  console.log(exists, logOutput);
  return exists;
}

testCases.forEach(testCase => {
  if (checkPathForTruthy(obj, testCase[0]) === testCase[1]) {
    console.log("Passed: " + testCase[0]);
  } else {
    console.log("Failed: " + testCase[0] + " expected " + testCase[1]);
  }
});

참고 URL : https://stackoverflow.com/questions/2631001/test-for-existence-of-nested-javascript-object-key

반응형