Je construis une fonction utilitaire qui devrait rechercher un nom de propriété et renvoyer sa valeur une fois trouvée. Cela devrait faire cela de manière récursive:
// Function
util.findVal = (object, propName) => {
for (let key in object) {
if (key === propName) {
console.log(propName)
console.log(object[key])
return object[key]
} else {
util.findVal(object[key], propName)
}
}
}
// Input
object: {
photo: {
progress: 20
}
}
// Usage
util.findVal(object, 'progress')
Cependant, le journal de la console est interminable et le navigateur se bloque. Qu'est-ce que je fais mal?
MODIFIER:
Voici comment j'appelle la fonction:
// Input
item: {
photo: {
file: {},
progress: 20
}
}
this.findProgress(item)
methods: {
findProgress (item) {
return util.findVal(item, this.propName)
}
}
Vous pouvez utiliser Object.keys
et effectuer une itération avec Array#some
.
function findVal(object, key) {
var value;
Object.keys(object).some(function(k) {
if (k === key) {
value = object[k];
return true;
}
if (object[k] && typeof object[k] === 'object') {
value = findVal(object[k], key);
return value !== undefined;
}
});
return value;
}
var object = { photo: { progress: 20 }};
console.log(findVal(object, 'progress'));
Votre code a quelques erreurs
Vous appelez récursivement util.findVal
mais ne renvoyez pas le résultat de l'appel. Le code devrait être return util.findVal(...)
Vous ne transmettez pas le nom d'attribut key
à l'appel récursif
Vous ne gérez pas la possibilité d'une boucle de référence
Si un objet contient une clé et qu'un sous-objet contenant la clé dont la valeur est renvoyée est aléatoire (dépend de la séquence dans laquelle les clés sont analysées)
Le troisième problème est ce qui peut causer une récursion infinie, par exemple:
var obj1 = {}, obj2 = {};
obj1.x = obj2; obj2.y = obj1;
si vous continuez à chercher de manière récursive, une recherche dans obj1
ou obj2
peut conduire à une récursion infinie.
Malheureusement, pour des raisons qui ne me semblent pas claires en Javascript, il est impossible de connaître l'objet "identité" ... (ce que fait Python id(x)
), vous ne pouvez comparer un objet à un autre. Cela signifie que pour savoir si un objet a déjà été vu, vous avez besoin d'une analyse linéaire avec des objets connus.
ES6 a également ajouté la possibilité de vérifier l'identité de l'objet avec
Set
etMap
, les objets pouvant être utilisés comme clés. Cela permet des temps de recherche plus rapides (sous-linéaires).
Une solution de recherche qui fonctionne en ordre de profondeur pourrait être par exemple
function findVal(obj, key) {
var seen = new Set, active = [obj];
while (active.length) {
var new_active = [], found = [];
for (var i=0; i<active.length; i++) {
Object.keys(active[i]).forEach(function(k){
var x = active[i][k];
if (k === key) {
found.Push(x);
} else if (x && typeof x === "object" &&
!seen.has(x) {
seen.add(x);
new_active.Push(x);
}
});
}
if (found.length) return found;
active = new_active;
}
return null;
}
étant donné qu'un objet et un nom d'attribut retournent toutes les valeurs trouvées avec ce nom à la première profondeur (il peut y avoir plus d'une valeur: par exemple, lorsque vous recherchez {x:{z:1}, y:{z:2}}
pour la clé "z"
, deux valeurs sont à la même profondeur).
La fonction gère également correctement les structures auto-référencées en évitant une recherche infinie.
Je pense que vous dites que vous voulez rechercher le nom de la propriété n'importe où récursivement dans l'arborescence des objets et des sous-propriétés. Si oui, voici comment j'aborderais ceci:
var object1 = _getInstance(); // somehow we get an object
var pname = 'PropNameA';
var findPropertyAnywhere = function (obj, name) {
var value = obj[name];
if (typeof value != 'undefined') {
return value;
}
foreach(var key in obj) {
var v2 = findPropertyAnywhere(obj[key], name);
if (typeof v2 != 'undefined') {
return v2;
}
}
return null;
}
findPropertyAnywhere(object1, pname);
Pensez-y s'il n'y a pas de clé trouvée.
Je pense que vous pourriez faire quelque chose comme ça au lieu de chercher
return object[propName] || null
Dans votre code, il manquait un point d'arrêt. J'imagine que vous essayez de rechercher dans l'objet entier et pas seulement dans les attributs directement associés. Voici donc une édition pour votre code
MODIFIER:
util.findVal = (object, propName) =>{
if(!!object[propName]){
return object[propName]
}else{
for (let key in object) {
if(typeof object[key]=="object"){
return util.findVal(object[key], propName)
}else{
return null
}
}
}
}
Une réponse dépend du degré de complexité que vous souhaitez obtenir. Par exemple, un tableau analysé JSON ne contient pas de fonctions - et je suis à peu près certain qu'il ne contiendra pas de valeur de propriété définie sur un nœud parent dans l'arborescence des objets.
Cette version renvoie la valeur de la propriété du premier nom de propriété trouvé lors de la recherche dans l'arborescence des objets. undefined
est renvoyé si la propriété nommée n'a pas été trouvée ou a une valeur de undefined
. Quelques modifications seraient nécessaires pour faire la différence. Il ne recherche pas à nouveau les nœuds parents déjà en cours de recherche, ni n'analyse les objets nuls!
let util = {};
util.findVal = (object, propName, searched=[]) => {
searched.Push( object)
for (let key in object) {
if (key === propName) {
return object[key]
}
else {
let obj = object[ key]
if( obj && (typeof obj == "object" || typeof obj == "function")) {
if( searched.indexOf(obj) >=0) {
continue
}
let found = util.findVal(obj, propName, searched)
if( found != searched) {
return found
}
}
}
}
searched.pop();
// not in object:
return searched.length ? searched : undefined
}
Je sais que c'est un ancien message, mais j'ai trouvé utile de résoudre un problème que j'avais avec la recherche récursive d'une valeur par sa clé. J'ai développé plus avant la réponse donnée par Nina Scholz et j'ai proposé ce qui suit. Il devrait être plus rapide car il ne crée pas un tableau de toutes les clés chaque fois qu'il est appelé de manière récursive. De plus, ceci retournera explicitement la valeur false si la clé n’est pas trouvée.
function findVal(obj, keyToFind) {
if (obj[keyToFind]) return obj[keyToFind];
for (let key in obj) {
if (typeof obj[key] === 'object') {
const value = findVal(obj[key], keyToFind);
if (value) return value;
}
}
return false;
}
var object = { photo: { progress: 20 }};
console.log(findVal(object, 'progress'));
Ancienne question, mais pour vérifier si la propriété existe n'importe où dans la hiérarchie d'un objet, essayez cette option simple
var obj = {
firstOperand: {
firstOperand: {
firstOperand: {
sweptArea: 5
}
}
}
};
function doesPropertyExists ( inputObj, prop )
{
return JSON.stringify(obj).indexOf( "\""+ prop +"\":" ) != -1;
};
console.log( doesPropertyExists( obj, "sweptArea" ) );
console.log( doesPropertyExists( obj, "firstOperand" ) );
console.log( doesPropertyExists( obj, "firstOperand22" ) );
J'ai fini par écrire cette fonction . C'est un refactor d'une fonction trouvée ici: Boucle récursive à travers un objet pour construire une liste de propriétés
ajout d'un paramètre de profondeur pour éviter le débordement de pile dans chrome devtools.
function iterate(obj, context, search, depth) {
for (var property in obj) {
if (Object.prototype.hasOwnProperty.call(obj, property)) {
if(typeof obj[property] == 'function') continue;
if( property == search ){
console.log(context+property);
return;
}
if (typeof obj[property] == "object" && depth < 7) {
//console.log('--- going in: ' + context+property);
iterate(obj[property], context+property+'.', search, depth+1);
}
/*else {
console.log(context+property);
}*/
}
}
}