118 votes

Recherche par clé profonde dans un tableau imbriqué

Disons que j'ai un objet :

[
    {
        'title': "some title"
        'channel_id':'123we'
        'options': [
                    {
                'channel_id':'abc'
                'image':'http://asdasd.com/all-inclusive-block-img.jpg'
                'title':'All-Inclusive'
                'options':[
                    {
                        'channel_id':'dsa2'
                        'title':'Some Recommends'
                        'options':[
                            {
                                'image':'http://www.asdasd.com'                                 'title':'Sandals'
                                'id':'1'
                                'content':{
                                     ...

Je veux trouver l'objet dont l'identifiant est 1. Existe-t-il une fonction pour quelque chose comme ça ? Je pourrais utiliser la fonction Underscore _.filter mais je devrais commencer par le haut et filtrer vers le bas.

1voto

Gerardlamo Points 408

Réponse améliorée pour prendre en compte les références circulaires au sein des objets. Elle affiche également le chemin parcouru pour y parvenir.

Dans cet exemple, je recherche un iframe que je sais se trouver quelque part dans un objet global :

const objDone = []
var i = 2
function getObject(theObject, k) {
    if (i < 1 || objDone.indexOf(theObject) > -1) return
    objDone.push(theObject)
    var result = null;
    if(theObject instanceof Array) {
        for(var i = 0; i < theObject.length; i++) {
            result = getObject(theObject[i], i);
            if (result) {
                break;
            }   
        }
    }
    else
    {
        for(var prop in theObject) {
            if(prop == 'iframe' && theObject[prop]) {
                i--;
                console.log('iframe', theObject[prop])
                return theObject[prop]
            }
            if(theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
                result = getObject(theObject[prop], prop);
                if (result) {
                    break;
                }
            } 
        }
    }
    if (result) console.info(k)
    return result;
}

Exécuter ce qui suit : getObject(reader, 'reader') a donné la sortie suivante et l'élément iframe à la fin :

iframe // (The Dom Element)
_views
views
manager
rendition
book
reader

REMARQUE : Le chemin est dans l'ordre inverse reader.book.rendition.manager.views._views.iframe

1voto

Richard T Points 11

J'aimerais suggérer un amendement à la réponse de Zach/RegularMike (mais je n'ai pas la "réputation" nécessaire pour pouvoir commenter !) J'ai trouvé que cette solution était une base très utile, mais elle a souffert dans mon application parce que s'il y a des chaînes de caractères dans les tableaux, il faudrait appeler récursivement la fonction pour chaque caractère dans la chaîne (ce qui faisait échouer les navigateurs IE11 et Edge avec des erreurs de type "out of stack space"). Mon optimisation simple a consisté à ajouter le même test utilisé dans l'appel récursif de la clause "object" à celui de la clause "array" :

if (arrayElem instanceof Object || arrayElem instanceof Array) {

Ainsi, mon code complet (qui recherche maintenant toutes les instances d'une clé particulière, donc légèrement différent de l'exigence initiale) est le suivant :

// Get all instances of specified property deep within supplied object
function getPropsInObject(theObject, targetProp) {
    var result = [];
    if (theObject instanceof Array) {
        for (var i = 0; i < theObject.length; i++) {
            var arrayElem = theObject[i];
            if (arrayElem instanceof Object || arrayElem instanceof Array) {
                result = result.concat(getPropsInObject(arrayElem, targetProp));
            }
        }
    } else {
        for (var prop in theObject) {
            var objProp = theObject[prop];
            if (prop == targetProp) {
                return theObject[prop];
            }
            if (objProp instanceof Object || objProp instanceof Array) {
                result = result.concat(getPropsInObject(objProp, targetProp));
            }
        }
    }
    return result;
}

1voto

Arfeo Points 182

Il y a quelque temps, j'ai fait une petite lib find-and qui est disponible sur npm pour travailler avec des objets imbriqués à la manière d'un lodash. Il y a le returnFound qui renvoie l'objet trouvé, ou un tableau d'objets s'il y a plus d'un objet trouvé.

Par exemple,

const findAnd = require('find-and');

const a = [
  {
    'title': "some title",
    'channel_id':'123we',
    'options': [
      {
        'channel_id':'abc',
        'image':'http://asdasd.com/all-inclusive-block-img.jpg',
        'title':'All-Inclusive',
        'options':[
          {
            'channel_id':'dsa2',
            'title':'Some Recommends',
            'options':[
              {
                'image':'http://www.asdasd.com',
                'title':'Sandals',
                'id':'1',
                'content':{},
              },
            ],
          },
        ],
      },
    ],
  },
];

findAnd.returnFound(a, {id: '1'});

renvoie à

{
  'image':'http://www.asdasd.com',
  'title':'Sandals',
  'id':'1',
  'content':{},
}

1voto

LuisP Points 1

Ce morceau de code vous permet d'obtenir tous les objets d'un JSON dont la clé est définie par l'utilisateur.

function main(obj = {}, property){
 const views = [];

 function traverse(o) {
    for (var i in o) {
      if(i === property) views.push(o[i]);
      if (!!o[i] && typeof(o[i])=="object") {
        console.log(i, o[i]);
        traverse(o[i]);
      } else {
        console.log(i, o[i]);
      }
    }
    }

  traverse(obj);
  return views;

}

Voici un exemple :

const obj = {
    id: 'id at level 1',
    level2: {
      id: 'id at level 2',
      level3: {
        id: 'id at level 3',
        level4: {
          level5: {
            id: 'id at level 5'
          }
       }
    }
  },
  text: ''
}

main(obj, 'id');

0voto

Nicolas de Lima Points 96
    function getPath(obj, path, index = 0) {
        const nestedKeys = path.split('.')
        const selectedKey = nestedKeys[index]

        if (index === nestedKeys.length - 1) {
            return obj[selectedKey]
        }

        if (!obj.hasOwnProperty(selectedKey)) {
            return {}
        }

        const nextObj = obj[selectedKey]

        return Utils.hasPath(nextObj, path, index + 1)
    }

Vous êtes les bienvenus Par : Gorillaz

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X