Fonction récursive

Bonjour,

je bloque sur une fonction récursive ... et pourtant j'en ai fait en javascript !

https://docs.google.com/spreadsheets/d/10gg-HgBljpVyRyDqadzNyCTAogRHHyWHhvp4rWYUqMQ/edit?usp=sharing

Voici le script actuel

var resultat = new Array(); 

function getAllDataJSON(url) {    
  var reponse = UrlFetchApp.fetch(url);
  var json = reponse.getContentText();
  var data = JSON.parse(json); 
  return getAllData(data);
}

function getAllData(obj){
  for (var property in obj) {
    if (obj[property] !== null) {
      if ((typeof obj[property] !== 'object') && (typeof obj[property] !== 'function')) {
        resultat.push( [ property , obj[property] ] )
      } else if (typeof obj[property] == 'object'){
        resultat.push( [ property , typeof obj[property] ] )
        //resultat.push( getAllData( obj[property] ) )
      };
    };
  };
  return resultat;
} 

je voudrais que, lorsque l'élément est un objet, je puisse le traiter par la fonction même qui l'a détectée, et ainsi jusqu'au plus bas niveau

getAllData(obj)

si quelqu'un voyait l'erreur car je tourne en rond !

Bonjour,

Une solution :

let resultat = [];

function getAllDataJSON(url) {
  getAllData(JSON.parse(UrlFetchApp.fetch(url).getContentText()));
  return resultat;
}

function getAllData(obj) {
  for (let p in obj) {
    obj[p] !== null && typeof obj[p] != 'object' && resultat.push([p, obj[p]]);
    typeof obj[p] == 'object' && getAllData(obj[p]);
  }
} 

Si c'est bien ce résultat que tu souhaites obtenir :

sheets extract data json

Génial, je vais regarder cela et comprendre pourquoi je n'y arrivais pas !

Je vais regarder maintenant comment ajouter le "chemin" (pseudo XPath) pour pouvoir ensuite accéder facilement à un seul objet. Exemple : data.records[1].fields.geo_point

et comprendre pourquoi je n'y arrivais pas !

Les 2 problèmes étaient les "return" et la fonction "getAllData" appelée dans "push()".

Je vais regarder maintenant comment ajouter le "chemin" (pseudo XPath) pour pouvoir ensuite accéder facilement à un seul objet. Exemple : data.records[1].fields.geo_point

Tu peux le faire exactement comme tu viens de l'écrire

function exemple(url) {
  const data = JSON.parse(UrlFetchApp.fetch(url).getContentText());
  return data.records[0].fields.geo_point;
}

En fait ce qui m'importe c'est de pouvoir détecter les "noms" des objets justement

Bon on le trouve quand même facilement ne serait-ce qu'en regardant le json sur firefox par exemple, mais j'avais fait un truc sur excel qui le permettait (et que je ne retrouve plus du reste, bigre)

J'ai mis l'objet en variable de la fonction

function exemple(url,chemin) {
  const data = JSON.parse(UrlFetchApp.fetch(url).getContentText());
  return eval(chemin);
}

Pour la récursivité, je n'ai toujours pas vu ce qui clochait, mais j'ai réussi à comprendre ton code, et à l'adapter en ajoutant le niveau et le nom de l'objet (bien que l'écriture ne soit pas aussi belle ! je n'imaginais pas qu'on puisse écrire un if de cette façon !)

let resultat = [];

function getAllDataPlusJSON(url) {
  var niv=1;
  getAllDataPlus(niv,JSON.parse(UrlFetchApp.fetch(url).getContentText()));
  return resultat;
}

function getAllDataPlus(niv,obj) {
  for (let p in obj) {
    obj[p] !== null && typeof obj[p] != 'object' && resultat.push([niv, p, obj[p]]);
    if (typeof obj[p] == 'object') {
      resultat.push([niv, p, '=>']);
      niv+=1;
      getAllDataPlus(niv,obj[p]);
      niv-=1;
    }
  }
} 

En passant, voila un exemple pour récupérer des données ciblées (dans ce cas : city, name, address, geo_point) :

function exemple2(url) {
  const data = JSON.parse(UrlFetchApp.fetch(url).getContentText());
  const resultat = [];
  data.records.forEach(r => resultat.push([r.fields.city, r.fields.name, r.fields.address, ...(r.fields.geo_point || [])]));
  return resultat;
}
sheets data city address geo point

Excellent !

Je prends souvent cet exemple d'url car on y trouve un "json gigogne", bref un élément string qui n'est rien d'autre qu'un json lui-même (timetable) !

30943 a9d23cc20cd1c2433e2850ce7880b396

Au passage, je ne comprends pas que l'usine à gaz que l'on peut trouver ici https://github.com/bradjasper/ImportJSON car in fine c'est simple !

Bon, j'ai enfin compris où était mon erreur initial : il ne fallait pas mettre resultat.push dans ... resultat.push( getAllData( obj[property] ) )

je vais maintenant adopter ton écriture qui est plus simple/condensée

Merci encore Sébastien pour ce super moment d'investigation !

Michel


edit : j'ai ajouté l'id de l'attribut et un paramètre (optionnel) qui est l'objet à explorer, mais j'ai dû adopter une écriture plus conventionnelle pour cela

let resultat = [];
function getAllDataJSON(url,objet) {
  if (objet == null || objet == '') {objet = "data"}
  const data = JSON.parse(UrlFetchApp.fetch(url).getContentText());
  getAllData(1,eval(objet),objet)
  return resultat
}
function getAllData(niv,obj,id) {
  const regex = new RegExp('[^0-9]+');
  for (let p in obj) {
    var newid = (regex.test(p)) ? id + '.' + p : id + '[' + p + ']';
    if (obj[p] !== null && typeof obj[p] != 'object'){
      resultat.push([niv, newid , p, obj[p]]);
    }
    if (typeof obj[p] == 'object') {
      resultat.push([niv, newid, p, "=>"]);
      niv+=1;
      getAllData(niv, obj[p], newid );
      niv-=1
    }
  }
}

Il est intéressant de voir qu'on peut commenter une fonction, et que ce commentaire apparaisse comme aide sur la feuille !

/**
 * Permet d'explorer un json ou un objet particulier du json.
 * Retourne niveau / id / attribut ou objet / valeur ou '=>'
 *
 * @param {string} url : url du site hébergeant le json
 * @param {string} objet : objet à explorer (optionnel) 
 * @customfunction
 */
function getAllDataJSON(url,objet) {
  if (objet == null || objet == '') {objet = "data"}
  const data = JSON.parse(UrlFetchApp.fetch(url).getContentText());
  getAllData(1,eval(objet),objet)
  return resultat
}

Une dernière prouesse ... pour mettre un json en tableau à partir d'un objet décrit en string ... avec une fonction prototypage sur les objets

ici j'ai ajouté le nom de l'attribut p en plus car tous ne sont pas toujours présents et peuvent décaler certaines cellules du tableau

J'ai donc ici en une dizaine de lignes l'équivalent de la fonction de github

avec

url = "https://public.opendatasoft.com/api/records/1.0/search/?dataset=prix_des_carburants_j_7&q=52000"
objet = "data.records"
et une extension fields ici directement dans le script
Object.prototype.element=function(i){return this[i]};
function importJSON(url,objet){
  var tableau = [];
  const data = JSON.parse(UrlFetchApp.fetch(url).getContentText());
  eval(objet).forEach(function(obj) {
    var prov = [];
    for (let p in obj.fields) {
      if (obj[p] !== null && typeof obj[p] != 'function') {
        prov.push(p+':: '+obj.fields.element(p))
      }
    }
    tableau.push(prov);
  });
  return tableau;
}

J'ai quasiment une fonction de type ImportJSON(url,XPath)

Rechercher des sujets similaires à "fonction recursive"