Test d'existence de la clé D'objet JavaScript imbriquée

Si j'ai une référence à un objet:

var test = {};

qui aura potentiellement (mais pas immédiatement) des objets imbriqués, quelque chose comme:

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

Quelle est la meilleure façon de tester l'existence de touches dans le plus profondément imbriqués les objets?

alert(test.level1); rendements undefined , mais alert(test.level1.level2.level3); échoue.

je suis en train de faire quelque chose comme ça:

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

mais je me demandais s'il y avait un meilleur moyen.

468
demandé sur Xufox 2010-04-13 19:47:06

30 réponses

Vous avez à faire étape par étape, si vous ne voulez pas un TypeError , parce que si l'un des membres est null ou undefined , et vous essayez d'accéder à un membre d'une exception sera levée.

Vous pouvez soit simplement catch l'exception, ou de faire une fonction pour tester l'existence de plusieurs niveaux, quelque chose comme:

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
301
répondu CMS 2015-01-25 15:45:36

voici un modèle I repris par Oliver Steele :

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

en fait, cet article entier est une discussion sur la façon dont vous pouvez le faire en javascript. Il s'installe en utilisant la syntaxe ci-dessus (qui n'est pas si difficile à lire une fois que vous vous y êtes habitué) comme un idiome.

295
répondu Gabe Moothart 2017-01-14 01:02:40

mise à Jour

ressemble à lodash a ajouté _.get pour tous vos besoins emboîtés de propriété.

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

https://lodash.com/docs#get


réponse précédente

lodash les utilisateurs peuvent profiter de la lodash.contrib qui a un couple méthodes qui atténuer ce problème .

getPath

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

Obtient la valeur à n'importe quelle profondeur dans un objet imbriqué fondée sur le chemin décrit par la remise des clés. Les clés peuvent être données sous forme de tableau ou de chaîne de caractères séparées par des points. Renvoie undefined si le chemin ne peut pas être atteint.

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
194
répondu Austin Pray 2017-04-26 14:32:01

j'ai fait tests de performance (merci cdMinix pour l'ajout de lodash) sur certaines des suggestions proposées à cette question avec les résultats énumérés ci-dessous.

clause de non-responsabilité N ° 1 faire des chaînes de caractères des références est une méta-programmation inutile et il est probablement préférable de l'éviter. Ne pas perdre la trace de vos références pour commencer. Lire la suite de cette réponse à un question similaire .

clause de non-responsabilité N ° 2 nous parlons ici de millions d'opérations par milliseconde. Il est très peu probable qu'aucun de ces ferait beaucoup de différence dans la plupart des cas d'utilisation. Choisissez celui qui a le plus de sens en connaissant les limites de chacun. Pour moi, je dirais quelque chose comme reduce par commodité.

Objet Wrap (par Oliver Steele) "1519120920 – - 34 – - le plus rapide

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

solution originale (suggérée dans la question) – 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;
}

_.obtenez de l' – 72%

deeptest – 86%

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

clowns tristes – 100% – le plus lent

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);
127
répondu unitario 2017-10-10 20:37:31

vous pouvez lire une propriété d'objet à n'importe quelle profondeur, si vous manipulez le nom comme une chaîne de caractères: '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');

Il retourne undefined si l'un des segments est undefined .

41
répondu kennebec 2014-11-28 00:49:10
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; })
);

si vous codez dans L'environnement ES6 (ou en utilisant 6to5 ) alors vous pouvez profiter de la fonction de flèche syntaxe:

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

en ce qui concerne la performance, il n'y a pas de pénalité de performance pour l'utilisation du bloc try..catch si la propriété est définie. Il y a un impact sur le rendement si la propriété est désactivée.

envisagez simplement d'utiliser _.has :

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

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

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

_.has(object, ['a', 'b', 'c']);
// → true
22
répondu Gajus 2016-01-31 13:06:20

Que diriez-vous de

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

}
17
répondu user187291 2010-04-13 16:09:08

j'ai essayé une approche récursive:

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

le ! keys.length || sort de la récursion pour ne pas exécuter la fonction sans aucune touche à tester. Essais:

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

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

Je l'utilise pour imprimer une vue HTML conviviale d'un groupe d'objets avec des clés/valeurs inconnues, par exemple:

var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
             ? myObj.MachineInfo.BiosInfo.Name
             : 'unknown';
8
répondu jrode 2013-11-07 23:00:58

ES6 réponse, soigneusement testé :)

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

→voir Codepen avec la pleine couverture de test

6
répondu Frank Nocke 2018-05-31 12:58:16

une façon simple est celle-ci:

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

le try/catch saisit les cas où l'un des objets de niveau supérieur tels que test, test.niveau 1, test.niveau1.le niveau2 n'est pas défini.

4
répondu jfriend00 2011-10-12 15:48:59

je pense que le script suivant donne une représentation plus lisible.

déclarer une fonction:

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

alors utilisez - le comme ceci:

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

}

Je l'appelle "technique du clown triste" parce qu'elle utilise le signe o (


EDIT:

voici une version pour TypeScript

il donne des contrôles de type à compilez le temps (ainsi que l'intellisense si vous utilisez un outil comme Visual Studio)

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

l'usage est le même:

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

mais cette fois intellisense fonctionne!

plus, vous pouvez définir une valeur par défaut:

o(o(o(o(o(test).level1).level2).level3, "none")
4
répondu VeganHunter 2017-01-13 00:16:16

Je n'ai vu aucun exemple de quelqu'un utilisant mandataires

alors j'ai trouvé la mienne. La grande chose à ce sujet est que vous n'avez pas à interpoler les chaînes. Vous pouvez réellement retourner une chaîne-able objet fonction et faire quelques choses magiques avec elle. Vous pouvez même appeler des fonctions et obtenir des index de tableaux pour vérifier les objets profonds

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

le code ci-dessus ça marche bien pour les trucs synchrones. Mais comment tester quelque chose d'asynchrone comme cet appel ajax? Comment avez-vous tester? et si la réponse n'est pas json quand elle renvoie une erreur http 500?

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

sûr que vous pouvez utiliser async/Wait pour se débarrasser de quelques callbacks. Mais si tu pouvais le faire encore plus par magie? quelque chose qui ressemble à ceci:

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

vous vous demandez probablement où sont toutes les chaînes promises et .then ... ça pourrait bloquer tout ce que vous savez... mais en utilisant la même technique de Proxy avec promesse vous pouvez réellement tester chemin complexe profondément imbriqué pour l'existence it sans jamais écrire une seule fonction

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
4
répondu Endless 2018-07-27 14:55:52

une version ES5 plus courte de l'excellente réponse de @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;
}

avec un essai similaire:

var test = { level1:{level2:{level3:'result'}}};
utils.checkObjHasKeys(test, ['level1', 'level2', 'level3']); // true
utils.checkObjHasKeys(test, ['level1', 'level2', 'foo']); // false
3
répondu mikemaccana 2012-05-21 09:28:25

basé sur cette réponse , j'ai trouvé cette fonction générique en utilisant ES2015 qui résoudrait le problème

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 );
}
3
répondu Alex Moldovan 2017-05-23 11:47:30

Vous pouvez également utiliser tc39 facultatif chaînage proposition avec babel 7 - tc39-proposition-facultatif chaîne

Le Code

ressemblerait à ceci:

  const test = test?.level1?.level2?.level3;
  if (test) alert(test);
3
répondu Goran.it 2018-06-14 07:59:56

la réponse donnée par CMS fonctionne bien avec la modification suivante pour les contrôles nuls aussi bien

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;
    }
2
répondu Anand Sunderraman 2013-06-21 18:01:37

les options suivantes ont été élaborées à partir de cette réponse . Même arbre pour les deux :

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

arrêter la recherche lorsqu'elle n'est pas définie

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

assurer chaque niveau un par un

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

$($(o.a).b).c // 1
$($(o.x).y).z // undefined
2
répondu leaf 2017-05-23 10:31:37

je sais que cette question est ancienne, mais je voulais offrir une extension en ajoutant ceci à tous les objets. Je sais que les gens ont tendance à désapprouver l'utilisation du prototype D'objet pour la fonctionnalité étendue de l'objet, mais je ne trouve rien de plus facile que de faire ça. De plus, c'est maintenant autorisé avec L'objet .defineProperty la méthode.

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

maintenant, afin de tester n'importe quelle propriété dans n'importe quel objet vous pouvez simplement faire:

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

voici un jsfiddle pour le tester, et en particulier il comprend un jQuery qui casse si vous modifiez l'objet.prototype directement en raison de la propriété devenant énumérable. Cela devrait fonctionner très bien avec les bibliothèques tierces.

2
répondu Brian Sidebotham 2015-03-24 15:57:04

je pense que c'est une légère amélioration (devient un 1-liner):

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

cela fonctionne parce que l'opérateur && renvoie l'opérande finale qu'il a évaluée (et ses courts-circuits).

2
répondu Julius Musseau 2015-07-16 19:05:27

cela fonctionne avec tous les objets et tableaux:)

ex:

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

c'est ma version améliorée de la réponse de Brian

j'ai utilisé _has comme le nom de la propriété, car il peut entrer en conflit avec l'existant a des biens (ex: les cartes)

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

voici le violon

2
répondu adutu 2016-02-24 14:08:40

Voici mon point de vue - la plupart de ces solutions ignorent le cas d'un réseau imbriqué comme dans:

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

je peux vouloir vérifier pour obj.l2[0].k

avec la fonction ci-dessous, vous pouvez faire deeptest('l2[0].k',obj)

la fonction retournera true si l'objet existe, false sinon

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));
2
répondu Mike D 2016-08-25 02:49:14

maintenant, nous pouvons aussi utiliser reduce pour passer à travers les clés imbriquées:

// @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)
2
répondu Egor Stambakio 2017-04-30 18:42:31

j'ai pensé ajouter un autre que j'ai inventé aujourd'hui. La raison pour laquelle je suis fier de cette solution est qu'il évite les crochets emboîtés qui sont utilisés dans de nombreuses solutions telles que objet Wrap (par Oliver Steele) :

(dans cet exemple, j'utilise un underscore comme variable de remplacement, mais n'importe quel nom de variable fonctionnera)

//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
}
2
répondu kubakoz 2017-08-11 22:10:33

il y a une fonction , ici sur thecodeabode (safeRead) qui va le faire de façon sécuritaire... c'est à dire

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

si une propriété est nulle ou non définie, une chaîne vide est retournée

1
répondu Ben 2013-04-13 08:50:57

basé sur un commentaire précédent , voici une autre version où l'objet principal n'a pas pu être défini non plus:

// Supposing that our property is at first.second.third.property:
var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;
1
répondu Juampy NR 2017-05-23 12:34:45

j'ai écrit ma propre fonction qui prend le chemin désiré, et a une bonne et mauvaise fonction de rappel.

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

Utilisation:

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
répondu Stephane LaFlèche 2015-03-19 17:55:03
//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
1
répondu alejandro 2015-07-10 14:27:02

je cherchais la valeur à retourner si la propriété existe, donc j'ai modifié la réponse DE CMS ci-dessus. Voici ce que j'ai trouvé:

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
1
répondu Noah Stahl 2015-07-25 03:13:03

j'avais le même problème et je voulais voir si je pouvais trouver ma propre solution. Ceci accepte le chemin que vous voulez vérifier en tant que chaîne.

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

voici un extrait avec quelques cas de journalisation et d'essai:

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]);
  }
});
1
répondu Jonathan 2016-09-14 11:08:26

modifier légèrement à cette réponse pour autoriser les tableaux imbriqués dans le chemin

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

vérifier la réponse liée pour les usages:)

1
répondu wesley chase 2017-06-22 02:16:55