Ceci est une page optimisée pour les mobiles. Cliquez sur ce texte pour afficher la vraie page.

POWERQUERY :: Combinaison de dossiers multiples non adjacents (non hiérarchiques) - Tables - Feuilles

Boostez vos compétences Excel avec notre communauté !

Rejoignez Excel Downloads, le rendez-vous des passionnés où l'entraide fait la force. Apprenez, échangez, progressez – et tout ça gratuitement ! 👉 Inscrivez-vous maintenant !

oguruma

XLDnaute Impliqué
Bonjour Le Forum,
Sous Excel/Powerquery il est possible de combiner plusieurs dossiers avec des dossiers adjacents (hiérarchiques). Cependant il est impossible via les assistants de combiner des dossiers se trouvant à plusieurs endroits du disques, c'est-à-dire non hiérarchiques ou non adjacents.
Cependant avec un peu de programmation en langage M c'est entièrement réalisable.
Une nouvelle fois, le code présenté dans les requêtes est aussi pédagogique pour les non initiés ou les débutants en langage M.
Il est largement documenté.

Ce post fait suite en fait à la série publiée ICI ainsi que les autres traitant des différents types de combinaisons.

Cela passe bien entendu par des tables de paramétrage. Elle ressemble (voir identique presque) à celle présentée dans les post traitant de ce sujet.


Ensuite il faut déclarer dans une table la liste des dossiers à explorer comme suit

Attention : les fichiers devront tous avoir le même de tableau structuré - il est répété sur chaque ligne.
Nous avons la possibilité d'identifier fonctionnellement le nom du dossier importé - exemple Dossier1 pour la 1ère ligne

Exemple de résultat


On a aussi la possibilité d'ajouter le nom du dossier importé par le paramètre AJOUT_DU_CHEMIN_COMPLET
Le placement en tête des colonnes DOSSIER et ENSEIGNE est piloté par les paramètres TAG_PREMIERE_COLONNE, TAG_COL_DOSSIER

Tout comme les autres post concernant ces combinaisons vous êtes libre dans le choix des colonnes

Il faut tout simplement respecter l'ordre.

Egalement de la même manière que les autres post il y a des contrôles d'existence sur les Item (Table, Sheet).

Petite nouveauté sympathique : en cas d'erreur de traitement exemple dossier inconnu on le retour suivant :


En effet D:\DOSSIERXXX n'existe pas sur mon disque.

Environnement PowerQueyr


RQ_IMPORT_DONNEES_01 : C'est la requête pas à pas qui a permet de construire la fonction fnImportMultipleFolders
RQ_IMPORT_DONNEES_01 :
C'est un exemple d'utilisation de la fonction fnImportMultipleFolders

Voici le code de la fonction

PowerQuery:
let fnImportMultipleFolders = (
    pType           as text,
    pPromote        as text,
    pTableCombine   as text,
    pTag            as text,
    pFolder         as text,
    pPath           as text
        ) as table => 
          
        let

            //******************************************************************************
            //                      FONCTIONS SECTION
            //******************************************************************************


            //******************************************************************************
            // Combinaison des dossiers
            //******************************************************************************
            COMBINE_FOLDERS = () as any =>
                // Dans cette fonction on balayer la liste des dossiers à importer contenus dans TBL_COMBINE_FOLDERS_TO_RECORDS
                    let
                        //***********************************************************************************************
                        // On va construire la liste des fichiers à combiner
                        //***********************************************************************************************
                        ListAccumlateCombineFolders = List.Accumulate(
                            LST_COMBINE_FOLDERS_TO_RECORDS,
                            {}, //#table({},{}),
                            (state, current) =>
                                    let
                                        Folder=Record.Field(current,LIST_COLUMNS_TBL_COMBINE{0}),  // On récupère le dossier en cours de lecture
                                        // Si le nom du dossier n'est pas renseigné on passe au suivant
                                        ListCombineFolders=if Folder <> null
                                                        then
                                                                let
                                                                    TblSourceFolder=Folder.Files(Folder),                      // Table contenant la liste des fichiers dans le dossier
                                                                    TblRemoveFields = Table.RemoveColumns(TblSourceFolder, {"Content", "Extension", "Date accessed", "Date modified", "Date created", "Attributes"}),
                                                                    // On récupère les noms des nouvelles colonnes qui seront ajoutées
                                                                    CurrentTag=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{1}),    // Tag fichier
                                                                    CurrentItem=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{2}),   // Item
                                                                    CurrentFolder=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{3}), // Tag dossier
                                                                    // Ajout des nouvelles colonnes
                                                                    TblAddTagColumn=Table.AddColumn(TblRemoveFields, LIST_COLUMNS_TBL_COMBINE{1}, each CurrentTag),     // Ajout colonne tag fichier
                                                                    TblAddItemColumn=Table.AddColumn(TblAddTagColumn, LIST_COLUMNS_TBL_COMBINE{2}, each CurrentItem),   // Ajout colonne Item
                                                                    TblAddTagFolder=Table.AddColumn(TblAddItemColumn, LIST_COLUMNS_TBL_COMBINE{3}, each CurrentFolder), // Ajout colonne tag dossier 
                                                                    TblAddTagPath=if STR_AJOUT_DU_CHEMIN_COMPLET = "OUI" then Table.AddColumn(TblAddTagFolder, "CHEMIN_COMPLET", each Folder) else TblAddTagFolder,
                                                                    // On construit la liste des fichiers qui seront combinés après exploration de tous les dossiers
                                                                    ListFilesToRecords=Table.ToRecords(TblAddTagPath),         // Liste des enregistrements - fichiers de la table
                                                                    ListCombineRecords=List.Combine({state, ListFilesToRecords}) // Liste finale des fichiers à traiter et à combiner entre-eux                         
                                                                in
                                                                    ListCombineRecords
                                                            else state // on passe au dossier suivant
                                    in
                                    ListCombineFolders
                            ),
                    //***********************************************************************************************
                    // On va combiner la liste des fichiers contruite au prélable
                    //***********************************************************************************************
                    TableAccumlateCombineFiles = List.Accumulate(
                            ListAccumlateCombineFolders,
                            #table({},{}),
                            (state,current) =>
                                    let
                                        PathName=current[Folder Path] & current[Name],                             
                                        // Récupération des valeurs Tag Colonne, Table(Item), Dossier
                                        TagColumn=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{1}),
                                        CurrentItem=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{2}),
                                        TagFolder=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{3}),
                                        Path=if STR_AJOUT_DU_CHEMIN_COMPLET = "OUI"  then PathName else null,
                                        // Test existence de l'Item
                                        isExistsIem = if CurrentItem is null then false else fnIsExistsTableSheet(PathName,CurrentItem, STR_TYPE),
                                        TableCombine=if isExistsIem
                                                    then
                                                        let
                                                            wbExcelTmp=try Excel.Workbook(File.Contents(PathName), null, true){[Item=CurrentItem,Kind=STR_TYPE]}[Data] otherwise state,
                                                            wbExcel=if STR_PROMOTE ="OUI" then Table.PromoteHeaders(wbExcelTmp, [PromoteAllScalars=true]) else wbExcelTmp,
                                                            TblAddTagColumn=Table.AddColumn(wbExcel, LIST_COLUMNS_TBL_COMBINE{1}, each TagColumn),     // Ajout colonne tag fichier                                       
                                                            TblAddTagFolder=Table.AddColumn(TblAddTagColumn, LIST_COLUMNS_TBL_COMBINE{3}, each TagFolder), // Ajout colonne tag dossier
                                                            TblAddPath=if STR_AJOUT_DU_CHEMIN_COMPLET = "OUI" then Table.AddColumn(TblAddTagFolder, "CHEMIN_COMPLET", each Path) else TblAddTagFolder, // Ajout colonne tag dossier
                                                            TableCombine=Table.Combine({state,TblAddPath})                                                 
                                                        in
                                                            TableCombine
                                                    else state,  // Si l'Item n'existe pas on passe au fichier suivant

                                        v= TableCombine
                                    in 
                                        v
                            )             
                    in
                    TableAccumlateCombineFiles,


            //******************************************************************************
            // Déplacement de l'étiquette à la 1ère colonne
            //******************************************************************************
            MOVE_COLUMN_TAG = (pTable as table, pCol as number) as any =>
                    let
                        LstColumns=Table.ColumnNames(pTable),
                        NbrColumnsTotal=List.Count(LstColumns),
                        NbColumns=if STR_AJOUT_DU_CHEMIN_COMPLET = "OUI" then NbrColumnsTotal - 3 else NbrColumnsTotal - 2,
                        L1={LIST_COLUMNS_TBL_COMBINE{pCol}},
                        L2=List.FirstN(LstColumns,NbColumns),
                        LstCombine=List.Combine({L1,L2}),
                        TblReorder=Table.ReorderColumns(pTable,LstCombine)
                    in
                    TblReorder, //TblReorder,
                      
            //******************************************************************************
            //                      PRINCIPALE SECTION
            //******************************************************************************

            //******************************************************************************
            // Identification des paramètres
            //******************************************************************************
            STR_TYPE=pType,
            STR_PROMOTE=pPromote,
            STR_TBL_COMBINE=pTableCombine,
            STR_TAG_PREM=pTag,
            STR_TAG_COL_DOSSIER=pFolder,
            STR_AJOUT_DU_CHEMIN_COMPLET=pPath,

            //******************************************************************************
            // On prépare les éléments pour la fusion
            //******************************************************************************

            // Message d'erreur si plantage en fin de traitement
            TBL_MSG_ERROR=#table(type table [STATUT=text, MSG=text, ERRNUM=Int64.Type],{{"ERREUR DE TRAITEMENT", "Vérifier le chemin de vos dossiers et votre paramétrage",99}}),

            // Ordre des colonnes de la table comportant la liste des dossiers à traiter
            // FICHIERS_EXCEL_COMBINE    ENSEIGNE    SHEET_COMBINE    DOSSIER
            TBL_COMBINE = Excel.CurrentWorkbook(){[Name=STR_TBL_COMBINE]}[Content],
            LIST_COLUMNS_TBL_COMBINE=Table.ColumnNames(TBL_COMBINE),
            STR_NOM_COLONNE_COMBINE=LIST_COLUMNS_TBL_COMBINE{0},  // Colonne comportant le nom du dossier à importer
            LST_COMBINE_FOLDERS_TO_RECORDS=Table.ToRecords(TBL_COMBINE),  // Transformation de la table en liste de Records

            //******************************************************************************
            // Fusion des fichiers
            //******************************************************************************
            TBL_DATA_COMBINE_FOLDERS=COMBINE_FOLDERS(),

            TBL_DATA_FINAL_01=if STR_TAG_PREM = "OUI" then MOVE_COLUMN_TAG(TBL_DATA_COMBINE_FOLDERS, 1) else TBL_DATA_COMBINE_FOLDERS,

            TBL_DATA_FINAL_02=try if STR_TAG_COL_DOSSIER = "OUI" then MOVE_COLUMN_TAG(TBL_DATA_FINAL_01, 3) else TBL_DATA_FINAL_01,

            HAS_ERROR=TBL_DATA_FINAL_02[HasError],

            TBL_TRY_02=if HAS_ERROR then TBL_MSG_ERROR else TBL_DATA_FINAL_02[Value]

        in
            TBL_TRY_02
in
    fnImportMultipleFolders

Je vous laisse construire vos jeux de tests ou utilisez et modifiez les fichiers de données fournis dans les post précédents.
 

Pièces jointes

Dernière édition:
Bonjour Le Forum,
Après réflexions... ce post mérite quelques explications en ce qui concerne le code de la fonction. En effet il comporte quelques parties complexes. Ces explications complémentaires devraient aider les débutants en langage M.

Récupération des paramètres
Là rien de compliqué. Ils sont passés à la fonction.
PowerQuery:
            //******************************************************************************
            // Identification des paramètres
            //******************************************************************************
            STR_TYPE=pType,
            STR_PROMOTE=pPromote,
            STR_TBL_COMBINE=pTableCombine,
            STR_TAG_PREM=pTag,
            STR_TAG_COL_DOSSIER=pFolder,
            STR_AJOUT_DU_CHEMIN_COMPLET=pPath,

PowerQuery:
let

    //******************************************************************************
    // Identification des paramètres
    //******************************************************************************
    STR_TYPE=fnGetParameter("TB_PARAMS", "TYPE"),
    STR_PROMOTE=Text.Upper(fnGetParameter("TB_PARAMS", "PROMOTE")),
    STR_TBL_COMBINE=fnGetParameter("TB_PARAMS", "TABLE_LIST_COMBINE"),
    STR_TAG_PREM=fnGetParameter("TB_PARAMS", "TAG_PREMIERE_COLONNE"), 
    STR_TAG_COL_DOSSIER=fnGetParameter("TB_PARAMS", "TAG_COL_DOSSIER"),
    STR_AJOUT_DU_CHEMIN_COMPLET=fnGetParameter("TB_PARAMS", "AJOUT_DU_CHEMIN_COMPLET"),
    TBL=fnImportMultipleFolders(STR_TYPE, STR_PROMOTE, STR_TBL_COMBINE, STR_TAG_PREM, STR_TAG_COL_DOSSIER, STR_AJOUT_DU_CHEMIN_COMPLET)

in
   TBL

Dans le code ci-dessous on prépare la fusion des différents dossiers à combiner.

PowerQuery:
   //******************************************************************************
    // On prépare les éléments pour la fusion
    //******************************************************************************

    // Message d'erreur si plantage en fin de traitement
    TBL_MSG_ERROR=#table(type table [STATUT=text, MSG=text, ERRNUM=Int64.Type],{{"ERREUR DE TRAITEMENT", "Vérifier le chemin de vos dossiers et votre paramétrage",99}}),

    // Ordre des colonnes de la table comportant la liste des dossiers à traiter
    // FICHIERS_EXCEL_COMBINE    ENSEIGNE    SHEET_COMBINE    DOSSIER
    TBL_COMBINE = Excel.CurrentWorkbook(){[Name=STR_TBL_COMBINE]}[Content],
    LIST_COLUMNS_TBL_COMBINE=Table.ColumnNames(TBL_COMBINE),
    STR_NOM_COLONNE_COMBINE=LIST_COLUMNS_TBL_COMBINE{0},  // Colonne comportant le nom du dossier à importer
    LST_COMBINE_FOLDERS_TO_RECORDS=Table.ToRecords(TBL_COMBINE),  // Transformation de la table en liste de Records

On prépare une table qui sera chargée d'afficher un message d'erreur en cas de plantage

PowerQuery:
 TBL_MSG_ERROR=#table(type table [STATUT=text, MSG=text, ERRNUM=Int64.Type],{{"ERREUR DE TRAITEMENT", "Vérifier le chemin de vos dossiers et votre paramétrage",99}})

Nous avons ici la liste des colonnes de la table des dossiers à traiter
PowerQuery:
    LIST_COLUMNS_TBL_COMBINE=Table.ColumnNames(TBL_COMBINE),


On constitue sous forme de Records la liste des dossiers à traiter

PowerQuery:
   LST_COMBINE_FOLDERS_TO_RECORDS=Table.ToRecords(TBL_COMBINE),  // Transformation de la table en liste de Records

pour obtenir ceci


Composition d'un record


PowerQuery:
STR_NOM_COLONNE_COMBINE=LIST_COLUMNS_TBL_COMBINE{0},

STR_NOM_COLONNE_COMBINE renvoie la colonne des noms de dossiers : FICHIERS_EXCEL_COMBINE

TBL_DATA_COMBINE_FOLDERS : Table résultante contenant la fusion de tous les fichiers contenus dans les dossiers.

On fait appel à la fonction :
PowerQuery:
COMBINE_FOLDERS = () as any =>

Nous allons la décomposer.
Elle est composée de deux parties de traitements.

1) on va constituer la liste des fichiers à traiter en explorant la liste des dossiers fournis dans LST_COMBINE_FOLDERS_TO_RECORDS
la liste des fichiers à traiter est contenue dans cette liste : ListAccumlateCombineFolders

2) On va ensuite explorer cette liste ListAccumlateCombineFolders pour combiner la liste des fichiers

Elle se présente comme ceci


Exemple d'un Record


L'étape suivante TableAccumlateCombineFiles va donc prendre le relais pour traiter les Records un à un afin de combiner tous les fichiers de cette liste

PowerQuery:
              TableAccumlateCombineFiles = List.Accumulate(
                    ListAccumlateCombineFolders,
                    #table({},{}),
                    (state,current) =>

Le code de cette étape :

PowerQuery:
              TableAccumlateCombineFiles = List.Accumulate(
                    ListAccumlateCombineFolders,
                    #table({},{}),
                    (state,current) =>
                            let
                                PathName=current[Folder Path] & current[Name],                              
                                // Récupération des valeurs Tag Colonne, Table(Item), Dossier
                                TagColumn=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{1}),
                                CurrentItem=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{2}),
                                TagFolder=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{3}),
                                Path=if STR_AJOUT_DU_CHEMIN_COMPLET = "OUI"  then PathName else null,
                                // Test existence de l'Item
                                isExistsIem = if CurrentItem is null then false else fnIsExistsTableSheet(PathName,CurrentItem, STR_TYPE),
                                TableCombine=if isExistsIem
                                             then
                                                let
                                                    wbExcelTmp=try Excel.Workbook(File.Contents(PathName), null, true){[Item=CurrentItem,Kind=STR_TYPE]}[Data] otherwise state,
                                                    wbExcel=if STR_PROMOTE ="OUI" then Table.PromoteHeaders(wbExcelTmp, [PromoteAllScalars=true]) else wbExcelTmp,
                                                    TblAddTagColumn=Table.AddColumn(wbExcel, LIST_COLUMNS_TBL_COMBINE{1}, each TagColumn),     // Ajout colonne tag fichier                                        
                                                    TblAddTagFolder=Table.AddColumn(TblAddTagColumn, LIST_COLUMNS_TBL_COMBINE{3}, each TagFolder), // Ajout colonne tag dossier
                                                    TblAddPath=if STR_AJOUT_DU_CHEMIN_COMPLET = "OUI" then Table.AddColumn(TblAddTagFolder, "CHEMIN_COMPLET", each Path) else TblAddTagFolder, // Ajout colonne tag dossier
                                                    TableCombine=Table.Combine({state,TblAddPath})                                                  
                                                in
                                                    TableCombine
                                             else state,  // Si l'Item n'existe pas on passe au fichier suivant

                                v= TableCombine
                            in  
                                v
                    )              
            in
              TableAccumlateCombineFiles,

current représente le Record en cours de lecture
state représente l'accumulateur : le résultat des traitements d'un enregistrement est mémorisé dans state ce qui permet de passer au Record suivant

Le chemin complet du fichier à combiner est constitué par PathName=current[Folder Path] & current[Name]

On récupère les éléments pour l'ajout des colonnes complémentaires "TAG"

PowerQuery:
                                TagColumn=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{1}),

                                CurrentItem=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{2}),

                                TagFolder=Record.Field(current, LIST_COLUMNS_TBL_COMBINE{3}),

On test l'existence de la table ou de la feuille - objet Item

PowerQuery:
                                isExistsIem = if CurrentItem is null then false else fnIsExistsTableSheet(PathName,CurrentItem, STR_TYPE),
                                TableCombine=if isExistsIem


Puis on ouvre le classeur Excel que l'on récupère dans une table wbExcel, on ajoute les colonnes "TAG" et enfin on combine le fichier avec le précédent TableCombine=Table.Combine({state,TblAddPath}).

Si c'est le premier il est combiné avec une table vide contenue dans le state initial à la 1ère itération

PowerQuery:
                                                    wbExcelTmp=try Excel.Workbook(File.Contents(PathName), null, true){[Item=CurrentItem,Kind=STR_TYPE]}[Data] otherwise state,
                                                    wbExcel=if STR_PROMOTE ="OUI" then Table.PromoteHeaders(wbExcelTmp, [PromoteAllScalars=true]) else wbExcelTmp,
                                                    TblAddTagColumn=Table.AddColumn(wbExcel, LIST_COLUMNS_TBL_COMBINE{1}, each TagColumn),     // Ajout colonne tag fichier                                        
                                                    TblAddTagFolder=Table.AddColumn(TblAddTagColumn, LIST_COLUMNS_TBL_COMBINE{3}, each TagFolder), // Ajout colonne tag dossier
                                                    TblAddPath=if STR_AJOUT_DU_CHEMIN_COMPLET = "OUI" then Table.AddColumn(TblAddTagFolder, "CHEMIN_COMPLET", each Path) else TblAddTagFolder, // Ajout colonne tag dossier
                                                    TableCombine=Table.Combine({state,TblAddPath})

LIST_COLUMNS_TBL_COMBINE{1} ou {2} ou {3} renvoie le nom de la colonne contenu dans la table des dossiers à explorer. Donc on peut voir là que les noms des colonnes n'ont aucune importance. En revanche il faut bien respecter l'ordre des colonnes.

Au final on obtient la liste des fichiers Excel combinés


et avec les colonnes "TAG" ajoutées


Il reste maintenant à réorganiser l'ordre des colonnes "TAG" si cela est demandé dans les paramètres généraux


Cette réorganisation est prise en charge par la fonction

PowerQuery:
    //******************************************************************************
    // Déplacement de l'étiquette à la 1ère colonne
    //******************************************************************************
    MOVE_COLUMN_TAG = (pTable as table, pCol as number) as any =>
            let
                LstColumns=Table.ColumnNames(pTable),
                NbrColumnsTotal=List.Count(LstColumns),
                NbColumns=if STR_AJOUT_DU_CHEMIN_COMPLET = "OUI" then NbrColumnsTotal - 3 else NbrColumnsTotal - 2,
                L1={LIST_COLUMNS_TBL_COMBINE{pCol}},
                L2=List.FirstN(LstColumns,NbColumns),
                LstCombine=List.Combine({L1,L2}),
                TblReorder=Table.ReorderColumns(pTable,LstCombine)
            in
               TblReorder,

On passe en paramètre la table à réorganiser et la position de la colonne à place en tête de la table résultat pour obtenir ceci



Enfin si les traitements ne se passent pas comme souhaité on affiche le message d'erreur

PowerQuery:
    TBL_DATA_FINAL_02=try if STR_TAG_COL_DOSSIER = "OUI" then MOVE_COLUMN_TAG(TBL_DATA_FINAL_01, 3) else TBL_DATA_FINAL_01,

    HAS_ERROR=TBL_DATA_FINAL_02[HasError],

    TBL_TRY_02=if HAS_ERROR then TBL_MSG_ERROR else TBL_DATA_FINAL_02[Value]

Exemple pour cette table



On aura comme résultat



A travers ces explications détaillées j'espère avoir aidé les grands débutants en langage M
 
Dernière édition:
- Navigue sans publicité
- Accède à Cléa, notre assistante IA experte Excel... et pas que...
- Profite de fonctionnalités exclusives
Ton soutien permet à Excel Downloads de rester 100% gratuit et de continuer à rassembler les passionnés d'Excel.
Je deviens Supporter XLD

Discussions similaires

Les cookies sont requis pour utiliser ce site. Vous devez les accepter pour continuer à utiliser le site. En savoir plus…