Developpez.com - 4D
X

Choisissez d'abord la catégorieensuite la rubrique :


Importer dans une liste hiérarchique

Date de publication : Janvier 2003

Par Olivier DESCHANELS (Responsable Programme 4D S.A.)
 

Dans cette note technique nous allons détailler pas à pas comment réaliser une liste hiérarchique à partir d'une table d'import.

I. Elements de base
II. Paramètres du problème
III. Construction de la méthode 4D
Méthode de test
Mise en place de la récursivité
Paramètre de retour
Premier appel récursif
Traitement du second niveau
Un peu de contrôle
Rendre générique
Mettre des bornes
Eviter les mélanges
IV. Aller plus loin
V. Base exemple


I. Elements de base

Dans nos vies de développeurs nous avons souvent eu à importer des données provenant de diverses sources. Pour cela, il est souvent pratique d'utiliser une table d'import temporaire, table qui sera ensuite relue par une méthode 4D chargée de répartir les informations aux emplacements adéquates dans la base de données.

Dans ce présent exercice la table [Import] est constituée de champs alphanumériques :

Pictures 0121x0200

Nous y importons les articles d'un magasin de sport :

Pictures 0636x0242

Comme nous pouvons le constater cette liste d'articles peut être parcourue de manière hiérarchique. Voici le résultat que nous désirons obtenir à la fin de cette note technique :

Pictures 0549x0365


II. Paramètres du problème

La lecture attentive de la liste ci-dessus nous donne quelques données du problème :

   • Les nombres de branches et d'éléments dans chacune sont variables ;

   • Le nombre de niveaux avant d'atteindre la fin d'une branche est variable ;

   • Une valeur peut se retrouver dans plusieurs branches ;

   • Les valeurs vides ne doivent pas être conservées.


Nous ajouterons que :

   • Nous ne connaissons pas le nombre maximal de champs à explorer afin de construire une méthode générique ;

   • Chaque niveau a au moins une valeur.


III. Construction de la méthode 4D

Le but de la méthode est de construire une liste hiérarchique. Dans 4D, une liste hiérarchique est manipulable par programmation via une référence de type entier long. Il semble donc naturel que la méthode 4D qui constituera le coeur de notre étude soit en fait une fonction retournant la référence de la liste hiérarchique construite. Cette méthode sera nommée "LH_Import".


Méthode de test

Dès à présent nous pouvons construire une méthode de test permettant de mettre en oeuvre notre travail :

      `méthode de test
   C_ENTIER LONG($liste)
   $liste:=LH_Import
   STOCKER LISTE($liste;"LH_import")

Cette méthode appelle la fonction "LH_Import", puis stocke le résultat dans une énumération du mode structure. Ainsi nous pouvons voir le résultat de notre travail sans avoir à créer la moindre interface.


Mise en place de la récursivité

Nous savons déja que la méthode "LH_Import" retourne une référence de liste hiérarchique. Comme vu également dans les paramètres du problème, la structure de la table est variable. Il est donc difficile de prévoir une méthode où le traitement est fixe. Au contraire nous avons besoin d'une méthode qui va s'adapter niveau par niveau.

L'expérience nous montre que dans ce cas là nous pouvons mettre en place un traitement récursif.

Dans un tel traitement, la methode s'appelle elle-même un certain nombre de fois ; à chaque passe, le résultat s'affine un peu plus jusqu'à ce qu'il soit parfait. A ce moment-là, la méthode s'arrête et retourne son résultat.

Dans la méthode de test, nous appelons la méthode "LH_Import" sans aucun paramètre. Sans être devin il est facile d'imaginer que dans les autres appels au moins un paramètre sera nécessaire pour passer des informations d'un cycle à un autre.

Nous pouvons donc distinguer deux cas :
   • Soit la méthode ne reçoit aucun paramètre et nous sommes alors dans le premier appel. Nous pouvons alors en profiter pour initialiser les éléments traités par la suite.
   • Soit la méthode reçoit un ou plusieurs paramètres et alors nous sommes en train de traiter la phase récursive.

Voici la traduction de ces deux cas en code 4D :


      `methode : LH_Import
   Au cas ou
      : (Nombre de parametres=0)
            `initialisation de la méthode
      Sinon
            `traitement de la récursivité
   Fin de cas


Paramètre de retour

La référence à une liste hiérarchique est un entier long. Ce sera notre paramètre de retour. Ce paramètre sera déclaré en en-tête de méthode, puis, on lui affectera une valeur dans chaque cas. Nous ne savons pas encore quelle valeur lui affecter mais nous pouvons néanmoins préparer le code :


      `methode : LH_Import
   C_ENTIER LONG($0)
      `liste hierarchique obtenue
   Au cas ou
      : (Nombre de parametres=0)
            `initialisation de la méthode
         $0:=
      Sinon
            `traitement de la récursivité
         $0:=
   Fin de cas


Premier appel récursif

Maintenant que la structure est en place nous allons traiter le premier appel à la méthode.

Lors de ce premier appel nous devons extraire de l'ensemble des enregistrements de la table [Import], les differentes valeurs pour le Champ1. A partir de cette liste de valeurs nous construisons la liste hiérarchique, ou plus exactement son premier niveau.

Pour travailler sur l'ensemble des enregistrements de la table, la commande TOUT SELECTIONNER convient de toute évidence. Pour extraire les différentes valeurs du Champ1 nous pouvons tirer bénéfice de la commande VALEURS DISTINCTES qui permet d'extraire toutes les valeurs d'un champ indexé ou indexable.

Pour construire la liste hierarchique nous mettrons en oeuvre les commandes Nouvelle Liste et AJOUTER A LISTE. Pour cette dernière, la documentation de 4D recommande d'associé à chaque élément une référence unique afin de faciliter les traitements par la suite. Pour suivre cette recommandation, nous allons mettre en place un compteur que nous initialiserons dans la phase adéquate puis que nous incrémenterons à chaque ajout d'élément dans la liste. Ce compteur sera matérialisé par une variable nommée lh_compteur. Cette variable ne peut pas être une variable locale car sinon à chaque appel de la méthode elle serait remise à zéro !

En regardant la liste de valeurs de la table [Import] nous commençons par lire le Champ1 puis nous passons au Champ2 et ainsi de suite ... Lors du premier appel nous commençons la lecture ; nous pouvons donc considérer que nous sommes au champ 0 (qui n'existe pas) et que nous allons passer au Champ1. Cette considération est une notion de niveau. Comme nous allons parcourir les niveaux les uns après les autres il semble logique de passer ce niveau en tant que paramètre lors de nos appels récursifs.


Voici tout ceci traduit en code 4D :


      `methode : LH_Import
   C_ENTIER LONG($0) `liste hierarchique obtenue
   Au cas ou
      : (Nombre de parametres=0)
         `initialisation de la méthode
         lh_compteur:=0
         $0:=LH_Import (0)
      Sinon
            `traitement de la récursivité
         TOUT SELECTIONNER([IMPORT])
         VALEURS DISTINCTES([IMPORT]Champ1;$_valeur)
         $liste:=Nouvelle liste
         Boucle ($i;1;Taille tableau($_valeur);1)
            lh_compteur:=lh_compteur+1
            AJOUTER A LISTE($liste;$_valeur{$i};lh_compteur)
         Fin de boucle
         $0:=$liste
   Fin de cas


Voici le résultat obtenu pour la liste hiérarchique :

Pictures 0380x0174


Traitement du second niveau

Le résultat est certe correct mais loin d'être complet !

Pour traiter le second niveau nous devons prendre une à une les valeurs du premier niveau puis rechercher les valeurs à affecter au second niveau.

Dans le langague 4D pour ajouter des élements fils à un élément parent, il faut associé à ce dernier une autre liste hiérarchique. Cette sous-liste sera associée lors d'ajout de l'élément parent à la liste en passant la référence de la sous-liste comme paramètre à la commande AJOUTER A LISTE.

Dans le traitement récursif nous devons à présent distinguer deux cas :

   - Soit nous sommes au premier passage et nous utilisons un TOUT SELECTIONNER comme précédement,

   - Soit nous sommes au second passage et nous devons utiliser un CHERCHER.

Ce CHERCHER a pour but de réduire le nombre d'enregistrements à ceux ayant une valeur précise pour le Champ1. Il nous faut donc cette valeur. Nous allons la passer en second paramètre. Nous devons donc modifier le code pour le premier appel. Le problème à ce niveau est que nous n'avons pas de valeur à passer. En fait ce n'est pas grave car le TOUT SELECTIONNER à suivre ne nécessite pas de valeur, nous pouvons donc passer n'importe quelle valeur. Pour ne pas perturber, nous passerons une chaîne vide.

Lors de la construction de la liste, nous devons donner la référence de la sous-liste associée à un élément en même temps que l'ajout de ce dernier à la liste. Nous devons donc calculer la sous-liste avant l'ajout, et ceci uniquement dans le cas du premier niveau (nous ne traitons pas encore le troisième niveau).


Voici le code 4D résultant :


      `methode : LH_Import

   C_ENTIER LONG($0)  `liste hierarchique obtenue
   C_ENTIER($1)  `Niveau dans la récursivité
   C_ALPHA(80;$2) `valeur

   Au cas ou
      : (Nombre de parametres=0)
            `initialisation de la méthode
         lh_compteur:=0
         $0:=LH_Import (0;"")
      Sinon
            `traitement de la récursivité
         Si ($1=0)
            TOUT SELECTIONNER([IMPORT])
            VALEURS DISTINCTES([IMPORT]Champ1;$_valeur)
         Sinon
            CHERCHER([IMPORT];[IMPORT]Champ1=$2)
            VALEURS DISTINCTES([IMPORT]Champ2;$_valeur)
         Fin de si

         $liste:=Nouvelle liste
         Boucle ($i;1;Taille tableau($_valeur);1)
            Si ($1=0)
               $sous_liste:=LH_Import (1;$_valeur{$i})
            Fin de si
            lh_compteur:=lh_compteur+1
            AJOUTER A LISTE($liste;$_valeur{$i};lh_compteur;$sous_liste;Vrai)
         Fin de boucle
         $0:=$liste
   Fin de cas


La liste obtenue à ce stade est la suivante :

Pictures 0373x0165


A ce niveau nous devons bien comprendre l'intérêt des tableau locaux $_valeur. En effet à chaque niveau ces tableaux sont réinitialisés (comme toutes variables locales) pour contenir les valeurs du niveau. Par contre lorsque nous retournons au niveau supérieur le tableau $_valeur contiendra toujours les valeurs qu'il contenait au moment ou nous l'avions quitté. La meilleure solution est de tracer la méthode et de suivre l'évolution du contenu des variables. Lorsque vous avez bien compris l'aspect local des variables, vous pouvez passer à la suite.


Un peu de contrôle

Notre liste commence à prendre tournure. Avant d'aller plus loin, nous allons mettre un peu de sécurité dans notre code. En effet nous n'avons pas traité le cas ou la commande VALEURS DISTINCTES retourne un tableau vide.

Si le tableau est vide, il n'y a aucune liste (ou sous liste) à construire ; il suffit de retourner une référence de liste égale à zéro et le travail est terminé.


Traduisons ceci en code 4D :


      `methode : LH_Import

   C_ENTIER LONG($0)  `liste hierarchique obtenue
   C_ENTIER($1)  `Niveau dans la récursivité

   Au cas ou
      : (Nombre de parametres=0)
            `initialisation de la méthode
         lh_compteur:=0
         $0:=LH_Import (0;"")

      Sinon
            `traitement de la récursivité
         Si ($1=0)
            TOUT SELECTIONNER([IMPORT])
            VALEURS DISTINCTES([IMPORT]Champ1;$_valeur)
         Sinon
            CHERCHER([IMPORT];[IMPORT]Champ1=$2)
            VALEURS DISTINCTES([IMPORT]Champ2;$_valeur)
         Fin de si

         Si (Taille tableau($_valeur)>0)
            $liste:=Nouvelle liste
            Boucle ($i;1;Taille tableau($_valeur);1)
               Si ($1=0)
                  $sous_liste:=LH_Import (1;$_valeur{$i})
               Fin de si
               lh_compteur:=lh_compteur+1
               AJOUTER A LISTE($liste;$_valeur{$i};lh_compteur;$sous_liste;Vrai)
            Fin de boucle
         Sinon
            $liste:=0
         Fin de si

         $0:=$liste
   Fin de cas


Rendre générique

Nous avons traiter les niveaux 1 et 2. Il est temps de rendre générique le code pour traiter automatiquement les niveaux suivants.

La première modification est au niveau de l'appel de la commande VALEURS DISTINCTES. A chaque niveau nous traitons le champ correspondant à ce niveau. Nous pouvons donc utiliser un pointeur construit pour obtenir la liste de valeurs. De même pour la recherche, nous cherchons toujours la valeur du champ correspondant au niveau précédent. Nous pouvons également utiliser un pointeur :

...
   $Niveau_traitement:=$1+1
   Si ($Niveau_traitement=1)
      TOUT SELECTIONNER([IMPORT])
   Sinon
      CHERCHER([IMPORT];Champ(Table(->[IMPORT]);$Niveau_traitement-1)->=$2)
   Fin de si
   VALEURS DISTINCTES(Champ(Table(->[IMPORT]);$Niveau_traitement)->;$_valeur)
....


Nous en profitons au passage pour définir la valeur $Niveau_traitement que nous pouvons utiliser pour l'appel récursif :

   $sous_liste:=LH_Import ($Niveau_traitement;$_valeur{$i})


Nous n'entourons plus cette ligne de code d'une condition. En effet nous voulons traiter à présent tous les niveaux. Voici donc le code modifié :


      `methode : LH_Import

   C_ENTIER LONG($0)  `liste hierarchique obtenue
   C_ENTIER($1)  `Niveau dans la récursivité

   Au cas ou
      : (Nombre de parametres=0)
            `initialisation de la méthode
         lh_compteur:=0
         $0:=LH_Import (0;"")

      Sinon
            `traitement de la récursivité
         $Niveau_traitement:=$1+1
         Si ($Niveau_traitement=1)
            TOUT SELECTIONNER([IMPORT])
         Sinon
            CHERCHER([IMPORT];Champ(Table(->[IMPORT]);$Niveau_traitement-1)->=$2)
         Fin de si
         VALEURS DISTINCTES(Champ(Table(->[IMPORT]);$Niveau_traitement)->;$_valeur)

         Si (Taille tableau($_valeur)>0)
            $liste:=Nouvelle liste
            Boucle ($i;1;Taille tableau($_valeur);1)
               $sous_liste:=LH_Import ($Niveau_traitement;$_valeur{$i})
               lh_compteur:=lh_compteur+1
               AJOUTER A LISTE($liste;$_valeur{$i};lh_compteur;$sous_liste;Vrai)
            Fin de boucle
         Sinon
            $liste:=0
         Fin de si

         $0:=$liste
   Fin de cas


Mettre des bornes

Si nous exécutons le code précédent nous obtenons des erreurs. En effet nous utilisons des pointeurs sur des champs sans contrôler si le champ existe ! Il nous faut donc mettre une protection à cette fin en comparant le niveau courant avec le nombre de champs dans la table.


      `methode : LH_Import

   C_ENTIER LONG($0)  `liste hierarchique obtenue
   C_ENTIER($1)  `Niveau dans la récursivité

   Au cas ou
      : (Nombre de parametres=0)
            `initialisation de la méthode
         lh_compteur:=0
         $0:=LH_Import (0;"")

      Sinon
            `traitement de la récursivité
         $Niveau_traitement:=$1+1
         Si ($Niveau_traitement<=Nombre de champs(Table(->[IMPORT])))
            Si ($Niveau_traitement=1)
               TOUT SELECTIONNER([IMPORT])
            Sinon
               CHERCHER([IMPORT];Champ(Table(->[IMPORT]);$Niveau_traitement-1)->=$2)
            Fin de si
            VALEURS DISTINCTES(Champ(Table(->[IMPORT]);$Niveau_traitement)->;$_valeur)

            Si (Taille tableau($_valeur)>0)
               $liste:=Nouvelle liste
               Boucle ($i;1;Taille tableau($_valeur);1)
                  $sous_liste:=LH_Import ($Niveau_traitement;$_valeur{$i})
                  lh_compteur:=lh_compteur+1
                  AJOUTER A LISTE($liste;$_valeur{$i};lh_compteur;$sous_liste;Vrai)
               Fin de boucle
            Sinon
               $liste:=0
            Fin de si
         Sinon
            $liste:=0
         Fin de si

         $0:=$liste
   Fin de cas


La liste obtenue est semblable à ceci :

Pictures 0441x0244

Nous avons des lignes vides car nous avons omis de traiter un des paramètres du problème : les valeurs vides ne doivent pas être conservées. Modifions donc le code pour traiter ce cas :


      `methode : LH_Import

   C_ENTIER LONG($0)  `liste hierarchique obtenue
   C_ENTIER($1)  `Niveau dans la récursivité

   Au cas ou
      : (Nombre de parametres=0)
            `initialisation de la méthode
         lh_compteur:=0
         $0:=LH_Import (0;"")

      Sinon
            `traitement de la récursivité
         $Niveau_traitement:=$1+1
         Si ($Niveau_traitement<=Nombre de champs(Table(->[IMPORT])))
            Si ($Niveau_traitement=1)
               TOUT SELECTIONNER([IMPORT])
            Sinon
               CHERCHER([IMPORT];Champ(Table(->[IMPORT]);$Niveau_traitement-1)->=$2)
            Fin de si
            VALEURS DISTINCTES(Champ(Table(->[IMPORT]);$Niveau_traitement)->;$_valeur)

            Si (Taille tableau($_valeur)>0)
               $liste:=Nouvelle liste
               Boucle ($i;1;Taille tableau($_valeur);1)
                  Si ($_valeur{$i}#"")
                     $sous_liste:=LH_Import ($Niveau_traitement;$_valeur{$i})
                     lh_compteur:=lh_compteur+1
                     AJOUTER A LISTE($liste;$_valeur{$i};lh_compteur;$sous_liste;Vrai)
                  Fin de si
               Fin de boucle
            Sinon
               $liste:=0
            Fin de si
         Sinon
            $liste:=0
         Fin de si

         $0:=$liste
   Fin de cas


Cette dernière modification se fait au sein de la boucle afin d'éliminer au plus tôt les valeurs inutiles.

Voici à présent la liste résultante :

Pictures 0450x0480


Eviter les mélanges

A première vue la liste précédente semble correcte. En fait elle n'est pas satisfaisante car les branches "Sport" sont erronées. Ce phénomene provient de la recherche effectuée qui est trop simple car elle se base sur une seule valeur.

Lorsque la ligne :

   CHERCHER([IMPORT];Champ(Table(->[IMPORT]);$Niveau_traitement-1)->=$2)

est effectuée avec le paramètre $2 à "Sport" nous obtenons 4 enregistrements au lieu de 3 dans un cas et un dans l'autre.

Pour éviter ce mélange nous devons faire la recherche sur tous les champs jusqu'au niveau en cours de traitement.

Le problème est de conserver les valeurs de tous ces champs. Pour cela nous allons utiliser une pile qui se matérialise sous la forme d'un tableau d'alphanumérique. A chaque niveau nous ajoutons dans le tableau la valeur courante du niveau en cours de traitement. La recherche précédente est modifiée pour tenir compte de toutes les valeurs de la pile.


Voici le code 4D révisé :


      `methode : LH_Import

   C_ENTIER LONG($0)  `liste hierarchique obtenue
   C_ENTIER($1)  `Niveau dans la récursivité

   Au cas ou
      : (Nombre de parametres=0)
            `initialisation de la méthode
         lh_compteur:=0
         TABLEAU ALPHA(80;_champ;Nombre de champs(Table(->[IMPORT])))
         $0:=LH_Import (0;"")

      Sinon
            `traitement de la récursivité
         $Niveau_traitement:=$1+1
         Si ($Niveau_traitement<=Nombre de champs(Table(->[IMPORT])))
            Si ($Niveau_traitement=1)
               TOUT SELECTIONNER([IMPORT])
            Sinon
               Boucle ($j;1;$1-1)
                  CHERCHER([IMPORT];Champ(Table(->[IMPORT]);$j)->=_champ{$j};*)
               Fin de boucle
               CHERCHER([IMPORT];Champ(Table(->[IMPORT]);$Niveau_traitement-1)->=$2)
            Fin de si
            VALEURS DISTINCTES(Champ(Table(->[IMPORT]);$Niveau_traitement)->;$_valeur)

            Si (Taille tableau($_valeur)>0)
               $liste:=Nouvelle liste
               Boucle ($i;1;Taille tableau($_valeur);1)
                  Si ($_valeur{$i}#"")
                     _champ{$Niveau_traitement}:=$_valeur{$i}
                     $sous_liste:=LH_Import ($Niveau_traitement;$_valeur{$i})
                     lh_compteur:=lh_compteur+1
                     AJOUTER A LISTE($liste;$_valeur{$i};lh_compteur;$sous_liste;Vrai)
                  Fin de si
               Fin de boucle
            Sinon
               $liste:=0
            Fin de si
         Sinon
            $liste:=0
         Fin de si

         $0:=$liste
   Fin de cas


Nous obtenons avec cette méthode la liste suivante :

Pictures 0438x0352

Cette liste est le résultat escompté ! CQFP (Ce qui fallait programmer).

La base exemple jointe est très simple. Vous lancez simplement la méthode "Test", puis visualisez le résultat dans le formulaire [Interface]Entrée.


IV. Aller plus loin

Pour aller plus loin nous pouvons imaginer de mettre en relief certains niveaux grace à des enregistrements de style, à l'ajout d'icône ... Nous sommes alors dans le traitement des listes hiérarchiques par programmation, et ceci est un autre sujet !


V. Base exemple

Téléchargez la base exemple :

Base pour Windows

Base pour Mac OS

__________________________________________________
Copyright © 1985-2009 4D SA - Tous droits réservés
Tous les efforts ont été faits pour que le contenu de cette note technique présente le maximum de fiabilité possible.
Néanmoins, les différents éléments composant cette note technique, et le cas échéant, le code, sont fournis sans garantie d'aucune sorte. L'auteur et 4D S.A. déclinent donc toute responsabilité quant à l'utilisation qui pourrait être faite de ces éléments, tant à l'égard de leurs utilisateurs que des tiers.
Les informations contenues dans ce document peuvent faire l'objet de modifications sans préavis et ne sauraient en aucune manière engager 4D SA. La fourniture du logiciel décrit dans ce document est régie par un octroi de licence dont les termes sont précisés par ailleurs dans la licence électronique figurant sur le support du Logiciel et de la Documentation afférente. Le logiciel et sa documentation ne peuvent être utilisés, copiés ou reproduits sur quelque support que ce soit et de quelque manière que ce soit, que conformément aux termes de cette licence.
Aucune partie de ce document ne peut être reproduite ou recopiée de quelque manière que ce soit, électronique ou mécanique, y compris par photocopie, enregistrement, archivage ou tout autre procédé de stockage, de traitement et de récupération d'informations, pour d'autres buts que l'usage personnel de l'acheteur, et ce exclusivement aux conditions contractuelles, sans la permission explicite de 4D SA.
4D, 4D Calc, 4D Draw, 4D Write, 4D Insider, 4ème Dimension ®, 4D Server, 4D Compiler ainsi que les logos 4e Dimension, sont des marques enregistrées de 4D SA.
Windows,Windows NT,Win 32s et Microsoft sont des marques enregistrées de Microsoft Corporation.
Apple, Macintosh, Power Macintosh, LaserWriter, ImageWriter, QuickTime sont des marques enregistrées ou des noms commerciaux de Apple Computer,Inc.
Mac2Win Software Copyright © 1990-2002 est un produit de Altura Software,Inc.
4D Write contient des éléments de "MacLink Plus file translation", un produit de DataViz, Inc,55 Corporate drive,Trumbull,CT,USA.
XTND Copyright 1992-2002 © 4D SA. Tous droits réservés.
XTND Technology Copyright 1989-2002 © Claris Corporation.. Tous droits réservés ACROBAT © Copyright 1987-2002, Secret Commercial Adobe Systems Inc.Tous droits réservés. ACROBAT est une marque enregistrée d'Adobe Systems Inc.
Tous les autres noms de produits ou appellations sont des marques déposées ou des noms commerciaux appartenant à leurs propriétaires respectifs.
__________________________________________________
 



Valid XHTML 1.1!Valid CSS!

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.
Contacter le responsable de la rubrique 4D