オブジェクトへの参照がある場合
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
が必要ない場合は、段階的に実行する必要があります。メンバーの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)
}
これがパターンI Oliver Steeleからピックアップ です。
var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );
実際、この記事全体は、JavaScriptでこれをどのように実行できるかについての議論です。彼は上記の構文(慣れれば読むのはそれほど難しくありません)を慣用句として使用することにしました。
Lodash が、ネストされたすべてのプロパティにニーズを満たすために_.get
を追加したように見えます。
_.get(countries, 'greece.sparta.playwright')
lodash ユーザーは lodash.contrib を楽しむことができます この問題を軽減する方法をいくつか組み合わせてください 。
署名:_.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を追加してくれてありがとう 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);
'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
}
const propExists = (obj, path) => {
return !!path.split('.').reduce((obj, prop) => {
return obj && obj[prop] ? obj[prop] : undefined;
}, obj)
}
また、babel 7と一緒にtc39オプションの連鎖提案を使用することもできます - 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などのツールを使用している場合は、インテリセンスも同様に)。
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")
それで私は自分自身を思い付きました。それについての素晴らしいところは、あなたが文字列を補間する必要がないということです。あなたは実際にチェーン可能を返すことができます 物 機能し、それを使って魔法のようなことをする。関数を呼び出して配列インデックスを取得して、深いオブジェクトをチェックすることもできます。
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
一つの簡単な方法はこれです:
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などの上位オブジェクトが定義されていない場合のケースをキャッチします。
この答え に基づいて、私は問題を解決する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 Gistをチェックすることができます: https://Gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues-js
短い、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
再帰関数を使ってこれを行うことができます。入れ子になった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;
}
これが私の考えです。これらの解決策のほとんどはネストした配列の場合を無視します
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));
私はこの質問が古いことを知っていますが、私はこれをすべてのオブジェクトに追加することによって拡張機能を提供したいと思いました。私は人々が拡張されたオブジェクト機能のために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が含まれています。これはサードパーティのライブラリではうまくいくはずです。
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)
これは少し改善されていると思います(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
以下のオプションは、この答え から始めて詳しく説明されています 。両方に同じツリー
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
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;
}
これはすべてのオブジェクトと配列で動作します。)
例:
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;
}});
これが なぞなぞです
前のコメント に基づいて、メインオブジェクトを定義できない別のバージョンがあります。
// Supposing that our property is at first.second.third.property:
var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;
安全な方法でこれを行う関数 がcodeabode(safeRead) にあります。
safeRead(test, 'level1', 'level2', 'level3');
いずれかのプロパティがnullまたは未定義の場合、空の文字列が返されます。
私は同じ問題を抱えていて、私が自分の解決策を思い付くことができるかどうかを見たかったです。これはチェックしたいパスを文字列として受け入れます。
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]);
}
});
//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 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
今日思いついたものをもう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
}
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);
}
パスにネストされた配列を許可するには、 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;
});
}
用法のためにリンクされた答えをチェックしてください:)
他の選択肢( この答えに近い ):
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
さらに、非常にコンパクトな別の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
プロセスを自動化しました
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;
}
別のES5ソリューション:
function hasProperties(object, properties) {
return !properties.some(function(property){
if (!object.hasOwnProperty(property)) {
return true;
}
object = object[property];
return false;
});
}
@ StephaneLaFlècheの回答 に基づいて、私はスクリプトの私の代替バージョンを思い付きました。
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
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/
入れ子になったプロパティをテストするために l33teralというライブラリを書きました 。あなたはこのようにそれを使うことができます:
var myObj = {/*...*/};
var hasNestedProperties = leet(myObj).probe('prop1.prop2.prop3');
ここでもES5/6ソリューションが好きです。
getValue (o, key1, key2, key3, key4, key5) {
try {
return o[key1][key2][key3][key4][key5]
} catch (e) {
return null
}
}
私が長い間使ってきた解決策(残念ながら文字列を使った方が良いとは言えない)
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');
オブジェクトとパスを "。"で区切って渡すことができます。
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
私は以下の方法で関数を使っています。
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"));
これは私が使用する小さなヘルパー関数です。私にとっては、これは非常に単純で直接的です。うまくいけば、それはいくつかのことに役立ちます:)。
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);
}
/**
* @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
最も簡単で簡単な答えは、次のとおりです。
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');
最初のレベルを配列にすることはできません。他のレベルを配列にすることはできません
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));
ここで私の文字列ベースの区切り文字のバージョン。
これには少しパターンがありますが、時には圧倒的なものになる可能性があります。一度に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.
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
かなりたくさんの答えがありますが、それでもなお、なぜ単純ではないのでしょうか?
値を取得する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の功績。
さらに別のバージョン:
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 .:再帰バージョンもあります。