J'ai une collection ÉNORME et je recherche une propriété par clé quelque part dans la collection. Quel est un moyen fiable d'obtenir une liste de références ou des chemins d'accès complets à tous les objets contenant cette clé/cet index? J'utilise jQuery et lodash si cela aide et vous pouvez oublier la récursion infinie du pointeur, c'est une pure réponse JSON.
fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "d");
// [o.c]
fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "e");
// [o.c.d]
fn({ 'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd');
// [o.cc,o.cc.dd]
fwiw lodash a une fonction _.find qui trouvera des objets imbriqués profonds de deux nids, mais il semble échouer après cela. (par exemple http://codepen.io/anon/pen/bnqyh )
Cela devrait le faire:
function fn(obj, key) {
if (_.has(obj, key)) // or just (key in obj)
return [obj];
// elegant:
return _.flatten(_.map(obj, function(v) {
return typeof v == "object" ? fn(v, key) : [];
}), true);
// or efficient:
var res = [];
_.forEach(obj, function(v) {
if (typeof v == "object" && (v = fn(v, key)).length)
res.Push.apply(res, v);
});
return res;
}
une solution JavaScript pure ressemblerait à ceci:
function findNested(obj, key, memo) {
var i,
proto = Object.prototype,
ts = proto.toString,
hasOwn = proto.hasOwnProperty.bind(obj);
if ('[object Array]' !== ts.call(memo)) memo = [];
for (i in obj) {
if (hasOwn(i)) {
if (i === key) {
memo.Push(obj[i]);
} else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) {
findNested(obj[i], key, memo);
}
}
}
return memo;
}
voici comment utiliser cette fonction:
findNested({'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd');
et le résultat serait:
[{x: 9}, {y: 9}]
cela va rechercher en profondeur un tableau d'objets (foin) pour une valeur (aiguille) puis retourner un tableau avec les résultats ...
search = function(hay, needle, accumulator) {
var accumulator = accumulator || [];
if (typeof hay == 'object') {
for (var i in hay) {
search(hay[i], needle, accumulator) == true ? accumulator.Push(hay) : 1;
}
}
return new RegExp(needle).test(hay) || accumulator;
}
Si vous pouvez écrire une fonction récursive en JS ordinaire (ou avec une combinaison de lodash) qui sera la meilleure (par performance), mais si vous voulez ignorer la récursivité de votre côté et que vous voulez opter pour un code simple et lisible (qui peut ne pas être le meilleur selon les performances), alors vous pouvez utiliser lodash # cloneDeepWith pour toutes les fins où vous devez parcourir un objet de manière récursive.
let findValuesDeepByKey = (obj, key, res = []) => (
_.cloneDeepWith(obj, (v,k) => {k==key && res.Push(v)}) && res
)
Ainsi, le rappel que vous passez comme 2ème argument de _.cloneDeepWith
parcourra récursivement tous les key/value
paires de façon récursive et tout ce que vous avez à faire est l'opération que vous voulez faire avec chacun. le code ci-dessus n'est qu'un exemple de votre cas. Voici un exemple de travail:
var object = {
prop1: 'ABC1',
prop2: 'ABC2',
prop3: {
prop4: 'ABC3',
prop5Arr: [{
prop5: 'XYZ'
},
{
prop5: 'ABC4'
},
{
prop6: {
prop6NestedArr: [{
prop1: 'XYZ Nested Arr'
},
{
propFurtherNested: {key100: '100 Value'}
}
]
}
}
]
}
}
let findValuesDeepByKey = (obj, key, res = []) => (
_.cloneDeepWith(obj, (v,k) => {k==key && res.Push(v)}) && res
)
console.log(findValuesDeepByKey(object, 'prop1'));
console.log(findValuesDeepByKey(object, 'prop5'));
console.log(findValuesDeepByKey(object, 'key100'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Avec Deepdash, vous pouvez pickDeep puis en obtenir chemins , ou indexer (créer un chemin-> objet de valeur)
var obj = { 'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}
var cherry = _.pickDeep(obj,"d");
console.log(JSON.stringify(cherry));
// {"cc":{"d":{}},"dd":{"d":{}}}
var paths = _.paths(cherry);
console.log(paths);
// ["cc.d", "dd.d"]
paths = _.paths(cherry,{pathFormat:'array'});
console.log(JSON.stringify(paths));
// [["cc","d"],["dd","d"]]
var index = _.indexate(cherry);
console.log(JSON.stringify(index));
// {"cc.d":{},"dd.d":{}}
Voici une démo Codepen
Quelque chose comme ça fonctionnerait, le convertissant en objet et se reproduisant.
function find(jsonStr, searchkey) {
var jsObj = JSON.parse(jsonStr);
var set = [];
function fn(obj, key, path) {
for (var prop in obj) {
if (prop === key) {
set.Push(path + "." + prop);
}
if (obj[prop]) {
fn(obj[prop], key, path + "." + prop);
}
}
return set;
}
fn(jsObj, searchkey, "o");
}
Violon: jsfiddle
Voici comment je l'ai fait:
function _find( obj, field, results )
{
var tokens = field.split( '.' );
// if this is an array, recursively call for each row in the array
if( obj instanceof Array )
{
obj.forEach( function( row )
{
_find( row, field, results );
} );
}
else
{
// if obj contains the field
if( obj[ tokens[ 0 ] ] !== undefined )
{
// if we're at the end of the dot path
if( tokens.length === 1 )
{
results.Push( obj[ tokens[ 0 ] ] );
}
else
{
// keep going down the dot path
_find( obj[ tokens[ 0 ] ], field.substr( field.indexOf( '.' ) + 1 ), results );
}
}
}
}
Le tester avec:
var obj = {
document: {
payload: {
items:[
{field1: 123},
{field1: 456}
]
}
}
};
var results = [];
_find(obj.document,'payload.items.field1', results);
console.log(results);
Les sorties
[ 123, 456 ]
Array.prototype.findpath = function(item,path) {
return this.find(function(f){return item==eval('f.'+path)});
}