web-dev-qa-db-fra.com

Underscore.js findWhere objets imbriqués

J'ai un objet de dossiers/fichiers qui ressemble à ceci:

{
  about.html : {
    path : './about.html'
  },
  about2.html : {
    path : './about2.html'
  },
  about3.html : {
    path : './about3.html'
  },
  folderName : {
    path : './folderName',
    children : {
      sub-child.html : {
        path : 'folderName/sub-child.html'
      }
    }
  }
}

Et cela peut aller jusqu'à 6-7 niveaux de dossiers avec des enfants.

Je veux trouver l'objet où path est égal à une chaîne que je fournis. Peu importe la profondeur.

J'utilise un trait de soulignement qui ne fait que le niveau supérieur:

_.findWhere(files,{path:'./about2.html'}

Comment puis-je faire une recherche profonde et imbriquée. Le soulignement a-t-il quelque chose pour cela ou dois-je créer un mix avec la récursion? 

10
wesbos

Ce n'est pas le code le plus joli, mais je l'ai testé et il semble fonctionner comme vous le demandez. Il est configuré en tant que mélange lodash/underscore, mais peut cependant être utilisé. L'utilisation serait comme ceci:

_.findDeep(testItem, { 'path': 'folderName/sub-child.html' })

La mise en oeuvre:

findDeep: function(items, attrs) {

  function match(value) {
    for (var key in attrs) {
      if(!_.isUndefined(value)) {
        if (attrs[key] !== value[key]) {
          return false;
        }
      }
    }

    return true;
  }

  function traverse(value) {
    var result;

    _.forEach(value, function (val) {
      if (match(val)) {
        result = val;
        return false;
      }

      if (_.isObject(val) || _.isArray(val)) {
        result = traverse(val);
      }

      if (result) {
        return false;
      }
    });

    return result;
  }

  return traverse(items);

}
11
dariusriggins

Au lieu de findWhere, utilisez filter, qui prend une fonction en tant que prédicat plutôt qu'une carte clé-valeur. Utilisez une fonction récursive pour vérifier le nœud actuel et les enfants possibles. Quelque chose comme ça:

var searchText = './about2.html';

var recursiveFilter = function(x) {
    return x.path == searchText || 
        ( typeof x.children != 'undefined' && recursiveFilter(x.children['sub-child.html']) );
};

_.filter(files, recursiveFilter);

Modifier  

En supposant que cela fonctionne, vous voudrez probablement créer une fonction getRecursiveFilter(searchText). Voici à quoi cela ressemblerait:

function getRecursiveFilter(searchText) { 
    var recursiveFilter = function(x) {
        return x.path == searchText || 
            (typeof x.children != 'undefined' 
                && arguments.callee(x.children['sub-child.html']) );
    };
    return  recursiveFilter;
}

Notez que ici, recursiveFilter utilise arguments.callee pour s’appeler récursivement .


Voici une démo de travail.

9
McGarnagle

Cela a déjà une réponse acceptée, mais cette autre réponse était très propre et parfaite pour ma situation similaire: https://stackoverflow.com/a/21600748/1913975_.filter + _.where

5
abenrob

Bien que la réponse acceptée fonctionne, elle est trop générique: elle recherche toutes les propriétés d'un objet pour rechercher des enfants. Je propose d'introduire un paramètre supplémentaire, appelé 'recursProperty', qui sera considéré comme allant au fond de l'objet. Cette solution est également configurée pour être utilisée comme mélange lodash/underscore et étend les capacités de chargement/soulignement.

_.findDeep = function(collection, predicate, recursProperty){
    let items = [];
    _.each(collection, each => items.Push(each));
    return _.find(items, function(value, key, coll){
        if (predicate(value, key, coll)){
            return true;
        } else {
            _.each(value[recursProperty], each => items.Push(each));
        }
    });
};

Il peut être utilisé comme toute autre fonction de soulignement. par exemple,

_.findDeep(self.baseEntities, baseEntity => baseEntity.id === 71, 'entity');

Ne pas fournir la valeur appropriée pour l'argument 'recursProperty' ou fournir null/undefined fera simplement que la recherche se fera uniquement au premier niveau (pas de profondeur).

1
adhirohah