web-dev-qa-db-ja.com

入れ子になった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);
}

しかし、もっと良い方法があるのではないかと私は思っていました。

545
user113716

TypeErrorが必要ない場合は、段階的に実行する必要があります。メンバーの1つが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

UPDATE 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)
}
339
CMS

これがパターンI Oliver Steeleからピックアップ です。

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

実際、この記事全体は、JavaScriptでこれをどのように実行できるかについての議論です。彼は上記の構文(慣れれば読むのはそれほど難しくありません)を慣用句として使用することにしました。

328
Gabe Moothart

更新

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
234
Austin Pray

この質問に対して提案されたいくつかの提案に対して、 パフォーマンステスト (lodashを追加してくれてありがとう cdMinix )しました。

免責事項#1文字列を参照に変換することは、不必要なメタプログラミングであり、おそらく最も避けられることです。最初にあなたの参照を見失ってはいけません。 同様の質問に対するこの答えからもっと詳しく

免責事項#2ここでは1ミリ秒あたり何百万もの操作について話しています。これらのいずれかが、ほとんどのユースケースで大きな違いを生むことはほとんどありません。それぞれの限界を知っているのが最も理にかなっている方を選択してください。私にとっては、便宜上reduceのようなものを使います。

オブジェクトラップ(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);
173
unitario

'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を返します。

43
kennebec
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
26
Gajus

どうですか

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

}
19
user187291

ES6の答え、徹底的にテストされた:)

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

を参照してください。完全なテストカバレッジを含むCodepen

16
Frank Nocke

また、babel 7と一緒にtc39オプションの連鎖提案を使用することもできます - tc39-proposal-optional-chaining

コードは次のようになります。

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

私は再帰的アプローチを試みました:

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';
9
jrode

次のスクリプトはもっと読みやすい表現になると思います。

関数を宣言する:

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

それから次のように使ってください。

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

}

サインoを使っているので、私はそれを「悲しい道化師のテクニック」と呼んでいます。


編集:

これはTypeScriptのバージョンです

コンパイル時に型チェックを行います(Visual Studioなどのツールを使用している場合は、インテリセンスも同様に)。

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

しかし、今度はインテリセンスが機能します。

さらに、デフォルト値を設定することができます。

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

誰かが プロキシを使っている例は見当たりませんでした

それで私は自分自身を思い付きました。それについての素晴らしいところは、あなたが文字列を補間する必要がないということです。あなたは実際にチェーン可能を返すことができます  機能し、それを使って魔法のようなことをする。関数を呼び出して配列インデックスを取得して、深いオブジェクトをチェックすることもできます。

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と.thenチェーンがどこにあるのか疑問に思うでしょう…これはあなたが知っていることすべてをブロックすることになるかもしれません…関数

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
6
Endless

一つの簡単な方法はこれです:

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

try/catchは、test、test.level1、test.level1.level2などの上位オブジェクトが定義されていない場合のケースをキャッチします。

5
jfriend00

この答え に基づいて、私は問題を解決する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 );
}
5
Alex Moldovan

安全にネストしたオブジェクトプロパティを取得するための小さな関数を作成しました。

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 Gistをチェックすることができます: https://Gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues-js

4
V. Sambor

短い、ES5バージョンの@ CMSの優れた答え:

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

再帰関数を使ってこれを行うことができます。入れ子になったObjectキーの名前がす​​べてわからなくてもこれは機能します。

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;
}
3
Ankit Arya

これが私の考えです。これらの解決策のほとんどはネストした配列の場合を無視します

    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));
3
Mike D

私はこの質問が古いことを知っていますが、私はこれをすべてのオブジェクトに追加することによって拡張機能を提供したいと思いました。私は人々が拡張されたオブジェクト機能のために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が含まれています。これはサードパーティのライブラリではうまくいくはずです。

3

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)
3
Egor Stambakio

これは少し改善されていると思います(1ライナーになります)。

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

これは、&&演算子が評価した最後のオペランドを返す(そして短絡する)ためです。

3
Julius Musseau

プロパティが存在する場合に返される値を探していたので、上記の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
3
Noah Stahl

以下のオプションは、この答え から始めて詳しく説明されています 。両方に同じツリー

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
3
leaf

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

これはすべてのオブジェクトと配列で動作します。)

例:

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

これはブライアンの答えの私の改良版です

プロパティ名として_ 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;
}});

これが なぞなぞです

3
adutu

前のコメント に基づいて、メインオブジェクトを定義できない別のバージョンがあります。

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

安全な方法でこれを行う関数 がcodeabode(safeRead) にあります。

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

いずれかのプロパティがnullまたは未定義の場合、空の文字列が返されます。

2
Ben

私は同じ問題を抱えていて、私が自分の解決策を思い付くことができるかどうかを見たかったです。これはチェックしたいパスを文字列として受け入れます。

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]);
  }
});
2
Jonathan
//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
2
alejandro

目的のパスをたどり、良いコールバック関数と悪いコールバック関数を持つ自分の関数を書きました。

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
2

今日思いついたものをもう1つ追加したいと思いました。私がこの解決策を誇りに思う理由は、 Object Wrap(by Oliver Steele) のような多くの解決策で使われているネストされた括弧を避けることです。

(この例では、プレースホルダー変数としてアンダースコアを使用していますが、どの変数名でも使用できます)

//the 'test' object
var test = {level1: {level2: {level3: 'level3'}}};

let _ = test;

if ((_=_.level1) && (_=_.level2) && (_=_.level3)) {

  let level3 = _;
  //do stuff with level3

}
//you could also use 'stacked' if statements. This helps if your object goes very deep. 
//(formatted without nesting or curly braces except the last one)

let _ = test;

if (_=_.level1)
if (_=_.level2)
if (_=_.level3) {

   let level3 = _;
   //do stuff with level3
}


//or you can indent:
if (_=_.level1)
  if (_=_.level2)
    if (_=_.level3) {

      let level3 = _;
      //do stuff with level3
}
1
kubakoz

HTMLテンプレートでone-linersを使用するのに実際には良い答えはありません。そのため、ES6プロキシを使用して作成しました。オブジェクトまたは値を「トラバース」関数に渡して、値またはフォールバック値を返す関数呼び出しで閉じたいだけの数のネストした呼び出しを行います。使用方法

const testObject = { 
  deep: { 
    nested: { 
      obj: { 
        closure: () => { return "closure" },
        number: 9,
        boolean: true,
        array: [1, 2, { foo: { bar: true } }]
      } 
    }
  }
}

traverse(testObject).deep() 
// {nested: {…}}

traverse(testObject).non.existent() 
// undefined

traverse(testObject).deep.nested.obj.closure()() 
// closure

traverse(testObject).deep.nested.obj.array[5]('fallback')
// fallback

traverse(testObject).deep.nested.obj.array[2]()
// {foo: {…}}

traverse(testObject).deep.nested.obj.array[2].foo.bar()
// true

traverse(testObject).deep.nested.obj.array[2].foo.bar[4]('fallback')
// fallback

traverse(testObject).completely.wrong[3].call().WILL_THROW()
// Uncaught TypeError: Cannot read property 'WILL_THROW' of undefined

機能自体:

const traverse = (input) => {
    // unique empty object
    const unset = new Object();
    // we need wrapper to ensure we have access to the same unique empty object
    const closure = (input) => {
        // wrap each input into this
        const handler = new Function();
        handler.input = input;    
        // return wrappers proxy 
        return new Proxy(handler, {
            // keep traversing
            get: (target, name) => {
                // if undefined supplied as initial input
                if (!target.input) {
                    return closure(unset);
                }
                // otherwise
                if (target.input[name] !== undefined) {
                    // input has that property
                    return closure(target.input[name]);
                } else {
                    return closure(unset);
                }
            },
            // result with fallback
            apply: (target, context, args) => {
                return handler.input === unset ? 
                    args[0] : handler.input;
            }
        })
    }
    return closure(input);    
}
1
pool

パスにネストされた配列を許可するには、 this answer に少し編集します。

var has = function (obj, key) {
    return key.split(".").every(function (x) {
        if (typeof obj != "object" || obj === null || !x in obj)
            return false;
        if (obj.constructor === Array) 
            obj = obj[0];
        obj = obj[x];
        return true;
    });
}

用法のためにリンクされた答えをチェックしてください:)

1
wesley chase

他の選択肢( この答えに近い ):

function resolve(root, path){
    try {
        return (new Function(
            'root', 'return root.' + path + ';'
        ))(root);
    } catch (e) {}
}

var tree = { level1: [{ key: 'value' }] };
resolve(tree, 'level1[0].key'); // "value"
resolve(tree, 'level1[1].key'); // undefined

もっと詳しく: https://stackoverflow.com/a/18381564/1636522

0
leaf

さらに、非常にコンパクトな別の1つです。

function ifSet(object, path) {
  return path.split('.').reduce((obj, part) => obj && obj[part], object)
}

と呼ばれる:

let a = {b:{c:{d:{e:'found!'}}}}
ifSet(a, 'b.c.d.e') == 'found!'
ifSet(a, 'a.a.a.a.a.a') == undefined

文字列を分割するため(呼び出しの読みやすさは向上します)、何も見つからないことが既に明らかである場合でも、すべてを繰り返し処理するため、パフォーマンスは向上しません(ただし、関数自体の読みやすさは向上します)。

少なくとも_.getより高速です http://jsben.ch/aAtmc

0
estani

プロセスを自動化しました

if(isset(object,["prop1","prop2"])){
// YES!

}

function isset(object, props){
    var dump;
    try {
        for(var x in props){
            if(x == 0) {
                dump = object[props[x]];
                return;
            }
            dump = dump[props[x]];
        }
    } catch(e) {
        return false;
    }

    return true;
}
0
Cedric Dugas

別のES5ソリューション:

function hasProperties(object, properties) {
    return !properties.some(function(property){
        if (!object.hasOwnProperty(property)) {
            return true;
        }
        object = object[property];
        return false;
    });
}
0
JKS

@ StephaneLaFlècheの回答 に基づいて、私はスクリプトの私の代替バージョンを思い付きました。

JSFiddleのデモ

var obj = {"a":{"b":{"c":"Hello World"}},"resTest":"potato","success":"This path exists"};
checkForPathInObject = function(object,path,value) {
        var pathParts   = path.split("."),
            result      = false;
        // Check if required parameters are set; if not, return false
        if(!object || typeof object == 'undefined' || !path || typeof path != 'string')
            return false;
        /* Loop through object keys to find a way to the path or check for value
         * If the property does not exist, set result to false
         * If the property is an object, update @object
         * Otherwise, update result */
        for(var i=0;i<pathParts.length;i++){
            var currentPathPart = pathParts[i];
            if(!object.hasOwnProperty( currentPathPart )) {
                result = false;
            } else if (object[ currentPathPart ] && path == pathParts[i]) {
                result = pathParts[i];
                break;
            } else if(typeof object[ currentPathPart ] == 'object') {
                object = object[ currentPathPart ];
            } else {
                result = object[ currentPathPart ];
            }
        }
        /* */
        if(typeof value != 'undefined' && value == result)
            return true;
        return result;
};
// Uncomment the lines below to test the script
// alert( checkForPathInObject(obj,'a.b.c') ); // Results "Hello World"
// alert( checkForPathInObject(obj,'a.success') ); // Returns false
// alert( checkForPathInObject(obj,'resTest', 'potato') ); // Returns true
0
davewoodhall

AngularJを使用している場合は、$ parseサービスを使用して、次のように深いオブジェクトプロパティが存在するかどうかを確認できます。

if( $parse('model.data.items')(vm) ) {
    vm.model.data.items.Push('whatever');
}

このような文を避けるために:

if(vm.model && vm.model.data && vm.model.data.items) {
    ....
}

あなたのコントローラに$ parseサービスを注入することを忘れないでください

詳細については、 https://glebbahmutov.com/blog/angularjs-parse-hacks/

0
Michiel

入れ子になったプロパティをテストするために l33teralというライブラリを書きました 。あなたはこのようにそれを使うことができます:

var myObj = {/*...*/};
var hasNestedProperties = leet(myObj).probe('prop1.prop2.prop3');

ここでもES5/6ソリューションが好きです。

0
Nicholas Cloud
getValue (o, key1, key2, key3, key4, key5) {
    try {
      return o[key1][key2][key3][key4][key5]
    } catch (e) {
      return null
    }
}
0
Sarpreet Singh

私が長い間使ってきた解決策(残念ながら文字列を使った方が良いとは言えない)

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

// way to use
if(get_if_exist('test.level1.level2.level3')) {
    alert(test.level1.level2.level3);
}

// or simply 
alert(get_if_exist('test.level1.level2.level3'));

編集:これはオブジェクト "test"がグローバルなスコープ/範囲を持つ場合にのみ機能します。それ以外の場合は、次のようにする必要があります。

// i think it's the most beautiful code I have ever write :p
function get_if_exist(obj){
    return arguments.length==1 || (obj[arguments[1]] && get_if_exist.apply(this,[obj[arguments[1]]].concat([].slice.call(arguments,2))));
}

alert(get_if_exist(test,'level1','level2','level3'));

最終バージョンを編集して、2つの呼び出し方法を許可します。

function get_if_exist(obj){
    var a=arguments, b=a.callee; // replace a.callee by the function name you choose because callee is depreceate, in this case : get_if_exist
    // version 1 calling the version 2
    if(a[1] && ~a[1].indexOf('.')) 
        return b.apply(this,[obj].concat(a[1].split('.')));
    // version 2
    return a.length==1 ? a[0] : (obj[a[1]] && b.apply(this,[obj[a[1]]].concat([].slice.call(a,2))));
}

// method 1
get_if_exist(test,'level1.level2.level3');


// method 2
get_if_exist(test,'level1','level2','level3');
0
Boris Dessy

オブジェクトとパスを "。"で区切って渡すことができます。

function checkPathExist(obj, path) {
  var pathArray =path.split(".")
  for (var i of pathArray) {
    if (Reflect.get(obj, i)) {
      obj = obj[i];
                
    }else{
                return false;
    }
  }
        return true;
}

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

console.log('level1.level2.level3 => ',checkPathExist(test, 'level1.level2.level3')); // true
console.log( 'level1.level2.foo => ',checkPathExist(test, 'level1.level2.foo')); // false
0
abdolmajid azad

私は以下の方法で関数を使っています。

var a = {};
a.b = {};
a.b.c = {};
a.b.c.d = "abcdabcd";

function isDefined(objectChainString) {
    try {
        var properties = objectChainString.split('.');
        var currentLevel = properties[0];
        if (currentLevel in window) {
            var consolidatedLevel = window[currentLevel];
            for (var i in properties) {
                if (i == 0) {
                    continue;
                } else {
                    consolidatedLevel = consolidatedLevel[properties[i]];
                }
            }
            if (typeof consolidatedLevel != 'undefined') {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    } catch (e) {
        return false;
    }
}

// defined
console.log(checkUndefined("a.b.x.d"));
//undefined
console.log(checkUndefined("a.b.c.x"));
console.log(checkUndefined("a.b.x.d"));
console.log(checkUndefined("x.b.c.d"));
0
Pratyush

これは私が使用する小さなヘルパー関数です。私にとっては、これは非常に単純で直接的です。うまくいけば、それはいくつかのことに役立ちます:)。

static issetFromIndices(param, indices, throwException = false) {
    var temp = param;

    try {
        if (!param) {
            throw "Parameter is null.";
        }

        if(!Array.isArray(indices)) {
            throw "Indices parameter must be an array.";
        }

        for (var i = 0; i < indices.length; i++) {
            var index = indices[i];
            if (typeof temp[index] === "undefined") {
                throw "'" + index + "' index is undefined.";
            }


            temp = temp[index];
        }
    } catch (e) {
        if (throwException) {
            throw new Error(e);
        } else {
            return false;
        }
    }

    return temp;
}

var person = {
    hobbies: {
        guitar: {
            type: "electric"
        }
    }
};

var indices = ["hobbies", "guitar", "type"];
var throwException = true;

try {
    var hobbyGuitarType = issetFromIndices(person, indices, throwException);
    console.log("Yay, found index: " + hobbyGuitarType);
} catch(e) {
    console.log(e);
}
0
brent schmidt
/**
 * @method getValue
 * @description simplifies checking for existance and getting a deeply nested value within a ceratin context
 * @argument {string} s       string representation of the full path to the requested property 
 * @argument {object} context optional - the context to check defaults to window
 * @returns the value if valid and set, returns undefined if invalid / not available etc.
 */
var getValue = function( s, context ){
    var fn = function(){
        try{
            return eval(s);
        }catch(e){
            return undefined;
        }
    }
    return fn.call(context||window,s);
}

および使用法:

if( getValue('a[0].b[0].b[0].d') == 2 ) // true
0
levi

最も簡単で簡単な答えは、次のとおりです。

var isDefinedPath = function (path) {

    var items = path.split ('.');

    if (!items || items.length < 1 || !(items[0] in window)) { return false; }

    var buffer = [items[0]];
    for (var i = 1, e = items.length; i < e; i ++) {
        buffer.Push (items[i]);
        if (eval ('typeof(' + buffer.join ('.') + ') == "undefined"')) {
            return false;
        }
    }

    return true;

}

test:isDefinedPath( 'level1.level2.level3');

最初のレベルを配列にすることはできません。他のレベルを配列にすることはできません

0
user667540
function isIn(string, object){
    var arr = string.split(".");
    var notFound = true;
    var length = arr.length;
    for (var i = 0; i < length; i++){
        var key = arr[i];
        if (!object.hasOwnProperty(key)){
            notFound = false;
            break;
        }
        if ((i + length) <= length){
            object = object[key];
        }
    }
    return notFound;
}
var musicCollection = {
    hasslehoff: {
        greatestHits : true
    }
};
console.log(isIn("hasslehoff.greatestHits", musicCollection));
console.log(isIn("hasslehoff.worseHits", musicCollection));

ここで私の文字列ベースの区切り文字のバージョン。

0
JFisher

これには少しパターンがありますが、時には圧倒的なものになる可能性があります。一度に2つか3つ入れ子にすることをお勧めします。

if (!(foo.bar || {}).weep) return;
// Return if there isn't a 'foo.bar' or 'foo.bar.weep'.

私が言及するのを忘れているかもしれませんが、あなたはこれをさらに拡張することもできます。以下の例は、ネストされたfoo.bar.weep.woopのチェックを示しています。利用可能なものがなければ、それは戻ります。

if (!((foo.bar || {}).weep || {}).woop) return;
// So, return if there isn't a 'foo.bar', 'foo.bar.weep', or 'foo.bar.weep.woop'.
// More than this would be overwhelming.
0
QSmally

CMSソリューションはうまく機能しますが、使用法/構文がより便利になることがあります。私は以下のことを提案します

var checkNested = function(obj, structure) {

  var args = structure.split(".");

  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
0
zainengineer

かなりたくさんの答えがありますが、それでもなお、なぜ単純ではないのでしょうか?

値を取得するes5バージョンは次のとおりです。

function value(obj, keys) {
    if (obj === undefined) return obj;
    if (keys.length === 1 && obj.hasOwnProperty(keys[0])) return obj[keys[0]];
    return value(obj[keys.shift()], keys);
}

if (value(test, ['level1', 'level2', 'level3'])) {
  // do something
}

value(config, ['applet', i, 'height']) || 42で使用することもできます

このアイデアを与えてくれた彼のES6ソリューションに対するCMSの功績。

0
iRaS

さらに別のバージョン:

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

nestedPropertyExists({a:{b:{c:1}}}, ['a','b','c']); // returns true
nestedPropertyExists({a:{b:{c:1}}}, ['a','b','c','d']); // returns false

これを解決するもう1つの方法は、たとえば次のような目的を持つことです。

var x = {
    a: {
        b: 3
    }
};

それから、このオブジェクトに次の関数を追加しました。

x.getKey = function(k){
        var r ;
        try {
            r = eval('typeof this.'+k+' !== "undefined"');
        }catch(e){
            r = false;
        }
        if(r !== false){
            return eval('this.'+k);
        }else{
            console.error('Missing key: \''+k+'\'');
            return '';
        }
    };

それからあなたはテストすることができます:

x.getKey('a.b');

未定義の場合、関数は ""(空の文字列)を返し、それ以外の場合は既存の値を返します。

リンクをチェックするこの他のより複雑な解決策についても検討してください。 JSオブジェクトにはプロパティディープチェックがあります

Object.prototype.hasOwnNestedProperty = function(propertyPath){
    if(!propertyPath)
        return false;

    var properties = propertyPath.split('.');
    var obj = this;

    for (var i = 0; i < properties.length; i++) {
        var prop = properties[i];

        if(!obj || !obj.hasOwnProperty(prop)){
            return false;
        } else {
            obj = obj[prop];
        }
    }

    return true;
};

// Usage: 
var obj = {
   innerObject:{
       deepObject:{
           value:'Here am I'
       }
   }
}

obj.hasOwnNestedProperty('innerObject.deepObject.value');

P.S .:再帰バージョンもあります。

0
Telmo Dias