ネストされたJavaScriptオブジェクトキーの存在をテストする


691

オブジェクトへの参照がある場合:

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

しかし、もっと良い方法があるかと思っていました。


1
最近尋ねられた接線に関連する質問を確認したい場合があります。stackoverflow.com
2525943 /


そこ命題のカップル:stackoverflow.com/a/18381564/1636522
葉の

level3プロパティがfalseの場合、現在のアプローチには潜在的な問題があります。その場合、プロパティが存在する場合でも、nfalseはこの例を参照してくださいjsfiddle.net/maz9bLjx
GibboK

10
単にあなたもtry catchを使用することができます
Raghavendra

回答:


487

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

ES6アップデート:

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

ただし、ネストされたプロパティの値を取得し、その存在を確認するだけではない場合は、次の簡単な1行の関数を使用します。

function getNested(obj, ...args) {
  return args.reduce((obj, level) => obj && obj[level], obj)
}

const test = { level1:{ level2:{ level3:'level3'} } };
console.log(getNested(test, 'level1', 'level2', 'level3')); // 'level3'
console.log(getNested(test, 'level1', 'level2', 'level3', 'length')); // 6
console.log(getNested(test, 'level1', 'level2', 'foo')); // undefined
console.log(getNested(test, 'a', 'b')); // undefined

上記の関数を使用すると、ネストされたプロパティの値を取得できますundefined。それ以外の場合はを返します。

アップデート2019-10-17:

オプションの連鎖提案は、上のステージ3に達したのECMAScript委員会のプロセス、これはあなたが安全にトークンを使用することで、深くネストされたプロパティにアクセスできるようになり、?.新たなオプションの連鎖演算子を

const value = obj?.level1?.level2?.level3 

アクセスされたレベルのいずれかが、nullまたはundefinedundefined自体で解決される場合。

この提案では、メソッド呼び出しを安全に処理することもできます。

obj?.level1?.method();

上記の式が生成されますundefined場合objobj.level1またはobj.level1.methodあるnullか、undefinedそれ以外の場合は、関数を呼び出します。

オプションのチェーンプラグインを使用して、Babelでこの機能を試すことができます

Babel 7.8.0以降、ES2020はデフォルトでサポートされています

この例をBabel REPLで確認してください。

🎉🎉更新:2019年12月🎉🎉

オプションの連鎖提案は、TC39委員会の2019年12月の会議で最終的にステージ4達しました。つまり、この機能はECMAScript 2020標準の一部になります。


4
arguments実際には配列ではありません。Array.prototype.slice.call(arguments)これを仮配列に変換します。学ぶ
ディーフォー

23
オブジェクトをコピーするよりも、これを実行して開始する方がはるかに効率的ですvar obj = arguments[0];var i = 1arguments
Claudiu

2
私は厳格さのためにtry / catchを使ってバージョンをまとめましたが、驚きではありません-パフォーマンスはひどいです(何らかの理由でSafariを除く)。以下に、かなりパフォーマンスの高いいくつかの回答と、選択した回答よりもパフォーマンスが大幅に高いClaudiuの変更点があります。jsperfはこちらjsperf.com/check-if-deep-property-exists-with-willnotthrow
netpoetica

3
ES6では、args変数宣言を削除...args して、checkNestedメソッドの2番目の引数として使用できます。developer.mozilla.org/en/docs/Web/JavaScript/Reference/...
バーノン

6
これは非常にメンテナンスが困難です。プロパティキーが変更された場合(変更される場合)、プロジェクトのすべての開発者はコードベース全体を「文字列検索」する必要があります。より大きな問題が発生するため、これは実際には問題の解決策ではありません
Drenai

356

これが私がOliver Steeleから拾ったパターンです:

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

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


8
@wared私はそれがどれほど簡潔であるかについて主に興味深いと思います。リンクされた投稿には、パフォーマンス特性の詳細な説明があります。はい、常にすべてのテストを実行しますが、一時変数の作成を回避します。毎回新しい空のオブジェクトを作成するオーバーヘッドを回避したい場合は、{}を変数にエイリアスできます。99%の場合、速度が重要であるとは期待していません。速度が重要な場合は、プロファイリングに代わるものはありません。
Gabe Moothart、2013

9
@MuhammadUmerいいえ、要点(test || {})は、テストが定義されていない場合は、あなたがやっているということです({}.level1 || {})。もちろん、{}.level1は未定義です。つまり、を実行しているということになります{}.level2
Joshua Taylor

3
@JoshuaTaylor:test宣言されていない場合はReferenceErrorが発生することを意味すると思いますが、宣言されていない場合は修正すべきバグがあるため問題ではありません。エラーは良いことです。

33
慣れてしまえば、それほど読みにくいものではない」とおっしゃっていました。まあ、これらはあなたすでにこれが混乱であることを知っている兆候です。次に、なぜこの解決策を提案するのですか?誤字が発生しやすく、読みやすさにまったく影響しません。見てください!私は場合する必要が醜い行を書き、それはする必要がありますasswell 読めます。だから私はただ固執するつもりだif(test.level1 && test.level1.level2 && test.level1.level2.level3)
Sharky

8
私が何かを欠落していない限り、これは偽の可能性があるブール型の終了プロパティでは機能しません...悲しいことに。そうでなければ、私はこのイディオムが大好きです。
T3db0t 2016年

261

更新

ネスト _.getれたすべてのプロパティ取得ニーズにlodash が追加されたようです。

_.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

Lodashは本当に_.isPathDefined(obj、pathString)メソッドを必要とします。
Matthew Payne

@MatthewPayneそれは多分いいでしょうが、それは本当に必要ではありません。自分で簡単にできるfunction isPathDefined(object, path) { return typeof _.getPath(object, path) !== 'undefined'; }
Thor84no

11
Lodashは、これと同じ機能自体を持っています_.get(countries, 'greece.sparta.playwright', 'default'); // → 'default' _.has(countries, 'greece.spart.playwright') // → false
トム・

_.result
Shishir Aroraは、

あなたが複数の異なるパスを決定する必要がある場合は考慮してください var url = _.get(e, 'currentTarget.myurl', null) || _.get(e, 'currentTarget.attributes.myurl.nodeValue', null) || null
サイモン・ハチソン

209

私が行っているパフォーマンステストを(ありがとうcdMinixを以下に示す結果とこの質問への提案の提案の一部にlodashを追加するため)。

免責事項#1文字列を参照に変換することは不必要なメタプログラミングであり、おそらく回避するのが最善です。そもそもあなたの参照を忘れないでください。同様の質問に対するこの回答から詳細を読んでください

免責事項#2ここでは、ミリ秒あたり数百万の操作について説明しています。これらのいずれかがほとんどのユースケースで大きな違いをもたらすことはほとんどありません。それぞれの制限を知っている最も意味のある方を選択してください。私にとっては、不便なようなもので行き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);

16
テストの%が多いほど、それが遅くなる
なだれ

2
ロダッシュは_.get()どうですか?これらの回答と比較して、どの程度パフォーマンスが良いですか?
beniutek

1
これらの各方法は、状況に応じて、他の方法よりも低速または高速です。すべてのキーが見つかった場合、「オブジェクトラップ」が最も速くなる可能性がありますが、キーの1つが見つからなかった場合は、「ネイティブソリューション/オリジナルソリューション」が速くなる可能性があります。
evilReiko 2017

1
@evilReikoどのメソッドもキーが見つからない場合は遅くなりますが、互いに比例していても、ほとんど同じです。ただし、あなたの言うとおりです。これは、他の何よりも知的演習です。ここでは、ミリ秒あたり100万回の反復について話します。それが大きな違いをもたらすユースケースは見当たりません。私個人的には、私は便利のために行くreducetry/catch、都合が悪いでしょう。
unitario 2017

パフォーマンスの比較try { test.level1.level2.level3 } catch (e) { // some logger e }
Lex

46

名前を文字列のように処理する場合、オブジェクトプロパティはどの深さでも読み取ることができます'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


3
この方法は、少なくともChromeでは非常に高いパフォーマンスを発揮し、場合によっては、選択した回答の@Claudiu修正バージョンよりもパフォーマンスが優れていることに注目してください。ここでパフォーマンステストを参照してください:jsperf.com/check-if-deep-property-exists-with-willnotthrow
netpoetica

28
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

2
私はそのtry-catchアプローチが最良の答えだと思います。オブジェクトのタイプを照会することと、APIが存在すると想定し、存在しない場合はそれに応じて失敗することには、哲学的な違いがあります。後者は、緩やかに型付けされた言語に適しています。stackoverflow.com/a/408305/2419669を参照してください。try-catchアプローチはまた、よりはるかに明確ですif (foo && foo.bar && foo.bar.baz && foo.bar.baz.qux) { ... }
ヤンミル理論

24

いかがですか

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

}

15
私は、try / catchがオブジェクトの存在をテストする良い方法だとは思いません。try/ catchは例外を処理するためのものであり、ここでのテストのような通常の状態ではありません。各ステップで(typeof foo == "undefined")の方が良いと思います。一般に、このような深くネストされたプロパティを使用している場合は、おそらくいくつかのリファクタリングが必要です。また、例外がスローされた場合、try / catchはFirebug(およびbreak-on-errorがオンになっているすべてのブラウザー)で中断を引き起こします。
Sam Dutton、2010年

私がこれに投票したのは、他のソリューションを使用した場合、ブラウザがその存在を2回チェックするためです。´acb = 2´を呼び出したいとしましょう。ブラウザー、値を変更する前に存在確認する必要があります(そうでない場合、OSがキャッチしたメモリエラーになります)。

4
問題はまだ残っています:ブラウザーの方がトライキャッチを設定するか、hasOwnProperty()n回呼び出すほうが高速です。

14
なぜこれが再び悪いのですか?これは私には最もきれいに見えます。
Austin Pray 2014年

私は言うでしょう:プロパティが存在することを期待している場合は、tryブロックにラップしても問題ありません。存在しない場合はエラーです。しかし、怠惰で、プロパティが存在しない場合のために通常のコードをcatchブロックに入れると、try / catchが誤用されます。ここでは、if / elseまたは同様のものが必要です。
robsch

18

ES6の回答、徹底的にテスト:)

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

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


フラットプロップの値を0に設定してテストを失敗させました。型強制に注意する必要があります。
ジェルマン

@germain これはあなたのために働きますか?(===異なるfalsys を明示的に比較し、テストを追加しました。より良いアイデアがある場合は、お知らせください)。
フランクノッケ2018年

フラットプロップの値をに設定して、テストを再度失敗させましたfalse。そして、あなたはあなたのオブジェクトに値を設定したいかもしれませんundefined(私はそれが奇妙なことを知っていますがJSです)。私は次のように設定された正の偽値を作成しました'Prop not Found'const hasTruthyProp = prop => prop === 'Prop not found' ? false : true const path = obj => path => path.reduce((obj, prop) => { return obj && obj.hasOwnProperty(prop) ? obj[prop] : 'Prop not found' }, obj) const myFunc = compose(hasTruthyProp, path(obj))
germain

私のコードペン(右上、簡単)をフォークして、テストを修正して追加し、あなたのURLを送ってくれませんか?ありがとう=)
フランク・ノッケ

(巨大な)サードパーティライブラリに逃げる...可能ですが、私の好みではありません。
フランクノッケ2018年

17

tc39オプションチェーンプロポーザルをbabel 7と一緒に使用することもできます-tc39 -proposal-optional-chaining

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

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

一部のTC39メンバーは異論があるため、この構文はほぼ確実に変更されることに注意してください。
jhpratt GOFUNDME RELICENSING 2018

おそらくしかし、これはいつか何らかの形で利用可能になるでしょう、そしてそれが重要な唯一のことです..それはJSで最も見逃している機能の1つです。
Goran.it 2018

11

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

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

次のスクリプトは、より読みやすい表現を提供すると思います。

関数を宣言します。

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")

1
°0o <°(())))><
ダニエルW.

1
これは正直で、Objectタイプがわからないときに「未定義」を顔に投げかけるので、これが好きです。+1。

1
ステートメントを括弧で
囲んでいる限り、

Sventiesに感謝します。コメント大好きです。これは非常に見やすい角度です。このような条件は主に「ifs」で使用され、常に外部ブラケットで囲まれています。だから、はい、それは実際に幸せなピエロです:)))
VeganHunter

あなたは本当にこれに行くために括弧に恋している必要があります...
Bastien7

7

グローバルfunctionを作成してプロジェクト全体で使用する

これを試して

function isExist(arg){
   try{
      return arg();
   }catch(e){
      return false;
   }
}

let obj={a:5,b:{c:5}};

console.log(isExist(()=>obj.b.c))
console.log(isExist(()=>obj.b.foo))
console.log(isExist(()=>obj.test.foo))

もし条件

if(isExist(()=>obj.test.foo)){
   ....
}

よく働く。シンプルで効率的
gbland777

6

簡単な方法の1つは次のとおりです。

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

try/catchより高いレベルのいずれかがそのような定義されていないtest.level1.level2試験、test.level1、などのオブジェクト場合のキャッチケース。


6

誰かがプロキシを使っている例は見なかった

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

上記のコードは同期的なものでうまく機能します。しかし、この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']

おそらくすべてのプロミスと.thenチェーンがどこにあるのだろう...これはあなたが知っていることすべてをブロックする可能性があります...しかし、プロミスと同じプロキシ技術を使用すると、単一の関数を書くことなく、その存在について深くネストされた複雑なパスを実際にテストできます


誰かが興味を持っている場合は、非同期バージョンをnpmで
Endless

5

この答えに基づいて、私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 );
}

1
これが私の最後のアプローチですfunction validChain (object, path) { return path.split('.').reduce((a, b) => (a || { })[b], object) !== undefined }
ジェームズハリントン

5

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

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

@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

これに関する唯一の問題は、未定義のキーのレベルが複数ある場合にTypeErrorが発生することです。例checkObjHasKeys(test, ['level1', 'level2', 'asdf', 'asdf']);
JKS

1
より適切なメソッドはeveryで、その値を直接返すことができます。
RobG

多分に変更success = false;return falseます。壊れていることがわかったら、救済する必要があります。nullまたはundefinedになると、これ以上深いものは存在できません。これにより、ネストが深いアイテムのエラーも防止できます。それらも明らかに存在しないためです。
2016年

4

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

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

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


4

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

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

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

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

各レベルを1つずつ確認してください

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

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

3

私はこの質問が古いことを知っていますが、これをすべてのオブジェクトに追加して拡張機能を提供したいと考えました。拡張されたオブジェクト機能のために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

これはすべてのオブジェクトと配列で機能します:)

例:

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

これが私の見解です-これらのソリューションのほとんどは、次のようにネストされた配列の場合を無視します。

    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

これで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

これは、再帰関数を使用して実行できます。これは、ネストされたすべてのオブジェクトキー名がわからない場合でも機能します。

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

2

安全な方法でこれを行うcodeabode(safeRead)の関数があります...すなわち

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

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


設定されていない場合は空の文字列を返すため、テンプレートを使用したこのメソッドが好きです
Lounge9

2

以前のコメントに基づいて、メインオブジェクトも定義できなかった別のバージョンを次に示します。

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

2

希望するパスを取得し、良いコールバック関数と悪いコールバック関数を持つ独自の関数を作成しました。

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

私はあなたのコードを私の答え
davewoodhall

2
//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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.