oguruma
XLDnaute Impliqué
Ce post a pour but de démontrer qu'il n'y a pas une seule et unique méthode pour renommer les colonnes d'une table dynamiquement. Les méthodes ici présentées ne sont certes pas exhaustives. 
Le second objectif de ce post est aussi de montrer les diverses capacités et techniques pour développer ses propres fonctions en PowerQuery ainsi que quelques astuces de programmation en langage M mettant en oeuvre des fonction peu connues. Ces techniques seront certainement d'une grande utilité pour ceux ou celles qui veulent se lancer dans le langage M.
Source des données exemples
Nous avons deux tables :
- une qui contient des colonnes à renommer
- une qui correspond à une table de correspondance pour le renommage des colonnes
Inventaires des requêtes et fonctions PowerQuery présentées
Table de correspondance TB_RENOM
Table des données TB_Source
Méthode 1
On passe par le classique List.Zip
	
	
	
	
	
		
LstNewNames=Expression.Evaluate("SourceRen[" & LstTblRename{1} & "]",[SourceRen=SourceRen]),
Cette ligne permet de gérer dynamiquement le nom de la colonne cible de correspondance sans ce soucier du nom de la colonne. Expression.Evaluate c'est en quelques sortes l'équivalent de la fonction INDIRECT sous Excel.
On récupère les anciens noms directement dans la table par cette ligne : LstOldNames=Table.ColumnNames(Source),
Puis la fonction List.Zip permet d'établir la correspondance entre les anciens et les nouveaux noms : List.Zip( { LstOldNames,LstNewNames } ),
Méthode 1 - améliorée
On ne va plus cherche les anciens noms dans la table des données mais dans la table de correspondance
	
	
	
	
	
		
Le calcul de la colonne à renommer est effectué de la même manière que la méthode 1 ci-dessus.
Méthode 2
Cette méthode comme les suivantes est plus étoffée et on ne passe pas par une table de correspondance. On va là intervenir sur la casse des noms de colonnes et le remplacement de caractères.
	
	
	
	
	
		
Même principe mais suppression d'une étape qui est remplacée par l'appel d'une fonction interne
	
	
	
	
	
		
La fonction interne est celle-ci
FunctionReplaceCase= (pElem as text) =>
let
ConvertCase=if Text.Upper(pCase) = "U" then
Text.Upper(pElem)
else if Text.Upper(pCase) = "L" then
Text.Lower(pElem)
else if Text.Upper(pCase) = "P" then
Text.Proper(pElem)
else null,
Replace=Text.Replace(ConvertCase, pStrFrom, pStrTo)
in
Replace,
Méthode 3 - elle donne les mêmes résultats mais elle est plus découpée en étapes afin d'avoir la main sur toutes les phases de transformation
	
	
	
	
	
		
Par cette variante on peut choisir le type de résultat à obtenir
	
	
	
	
	
		
Méthode 4 - ajout de préfixe ou de suffixe
	
	
	
	
	
		
Utilisation des fonctions
	
	
	
	
	
		
	
		
			
		
		
	
				
			Le second objectif de ce post est aussi de montrer les diverses capacités et techniques pour développer ses propres fonctions en PowerQuery ainsi que quelques astuces de programmation en langage M mettant en oeuvre des fonction peu connues. Ces techniques seront certainement d'une grande utilité pour ceux ou celles qui veulent se lancer dans le langage M.
Source des données exemples
Nous avons deux tables :
- une qui contient des colonnes à renommer
- une qui correspond à une table de correspondance pour le renommage des colonnes
Inventaires des requêtes et fonctions PowerQuery présentées
Table de correspondance TB_RENOM
Table des données TB_Source
Méthode 1
On passe par le classique List.Zip
		PowerQuery:
	
	
	let fnRename = (pSource as any, pSourceRen as any ) as table =>
    let
        Source=if pSource is text then
        Excel.CurrentWorkbook(){[Name=pSource]}[Content]
        else pSource,
        SourceRen=if pSourceRen is text then
        Excel.CurrentWorkbook(){[Name=pSourceRen]}[Content]
        else pSourceRen,
        LstOldNames=Table.ColumnNames(Source),
        LstTblRename=Table.ColumnNames(SourceRen),
        LstNewNames=Expression.Evaluate("SourceRen[" & LstTblRename{1} & "]",[SourceRen=SourceRen]),
        RenameColumnsV1=Table.RenameColumns( Source,
                        List.Zip( { LstOldNames,LstNewNames } ),
                        MissingField.Ignore )                       
    in
        RenameColumnsV1
in
    fnRenameLstNewNames=Expression.Evaluate("SourceRen[" & LstTblRename{1} & "]",[SourceRen=SourceRen]),
Cette ligne permet de gérer dynamiquement le nom de la colonne cible de correspondance sans ce soucier du nom de la colonne. Expression.Evaluate c'est en quelques sortes l'équivalent de la fonction INDIRECT sous Excel.
On récupère les anciens noms directement dans la table par cette ligne : LstOldNames=Table.ColumnNames(Source),
Puis la fonction List.Zip permet d'établir la correspondance entre les anciens et les nouveaux noms : List.Zip( { LstOldNames,LstNewNames } ),
Méthode 1 - améliorée
On ne va plus cherche les anciens noms dans la table des données mais dans la table de correspondance
		PowerQuery:
	
	
	let fnRename = (pSource as any, pSourceRen as any ) as table =>
    let
        Source=if pSource is text then
        Excel.CurrentWorkbook(){[Name=pSource]}[Content]
        else pSource,
        SourceRen=if pSourceRen is text then
        Excel.CurrentWorkbook(){[Name=pSourceRen]}[Content]
        else pSourceRen,
        LstTblRename=Table.ColumnNames(SourceRen),   
        LstOldNames=Expression.Evaluate("SourceRen[" & LstTblRename{0} & "]",[SourceRen=SourceRen]),
        LstNewNames=Expression.Evaluate("SourceRen[" & LstTblRename{1} & "]",[SourceRen=SourceRen]),
        RenameColumnsV1=Table.RenameColumns( Source,
                        List.Zip( { LstOldNames,LstNewNames } ),
                        MissingField.Ignore )                       
    in
        RenameColumnsV1
in
    fnRenameLe calcul de la colonne à renommer est effectué de la même manière que la méthode 1 ci-dessus.
Méthode 2
Cette méthode comme les suivantes est plus étoffée et on ne passe pas par une table de correspondance. On va là intervenir sur la casse des noms de colonnes et le remplacement de caractères.
		PowerQuery:
	
	
	//-------------------------------------------------------------------------------------------------------------------------
// Renommage des colonnes d'une table avec remplacement de caractères et modification de la casse
//-------------------------------------------------------------------------------------------------------------------------
let fnConvertTableColumnsNames = (pSource as any,pStrFrom as text, pStrTo as text, optional pCase as text) as table =>   
    let
        //------------------------------------------------------------------------
        // Identification de la source
        //------------------------------------------------------------------------
        pTable=if pSource is text then
                Excel.CurrentWorkbook(){[Name=pSource]}[Content]
                else pSource,       
        //------------------------------------------------------------------------
        // Remplacement des caractères demandés
        //------------------------------------------------------------------------
        TransformSource = Table.RenameColumns(pTable
                        ,List.Transform(Table.ColumnNames(pTable),
                                        each {_, Text.Replace(_, pStrFrom, pStrTo)}
                                        )
                        ),
        //------------------------------------------------------------------------
        // Remplacement de la casse
        //------------------------------------------------------------------------
        CaseSource = if Text.Upper(pCase) = "U" then
                        Table.TransformColumnNames(TransformSource, each Text.Upper(_))
                        else if Text.Upper(pCase) = "L" then
                                Table.TransformColumnNames(TransformSource, each Text.Lower(_))
                             else if Text.Upper(pCase) = "P" then
                                  Table.TransformColumnNames(TransformSource, each Text.Proper(_))
                                else TransformSource
    in
        CaseSource
in
    fnConvertTableColumnsNamesMême principe mais suppression d'une étape qui est remplacée par l'appel d'une fonction interne
		PowerQuery:
	
	
	//-------------------------------------------------------------------------------------------------------------------------
// Renommage des colonnes d'une table avec remplacement de caractères et modification de la casse
//-------------------------------------------------------------------------------------------------------------------------
let fnConvertTableColumnsNames = (pSource as any,pStrFrom as text, pStrTo as text, optional pCase as text) as table =>   
    let
        //-------------------------------------------------------------------
        // Construction d'une fonction chargée du remplacement de la casse
        //-------------------------------------------------------------------
        FunctionReplaceCase= (pElem as text) =>
            let
                ConvertCase=if Text.Upper(pCase) = "U" then
                    Text.Upper(pElem)
                    else if Text.Upper(pCase) = "L" then
                            Text.Lower(pElem)
                            else if Text.Upper(pCase) = "P" then
                                    Text.Proper(pElem)
                                    else null,
            Replace=Text.Replace(ConvertCase, pStrFrom, pStrTo)
            in
                Replace,   
        //-------------------------------------------------------------------
        // Source des données
        //-------------------------------------------------------------------
        pTable=if pSource is text then
                Excel.CurrentWorkbook(){[Name=pSource]}[Content]
                else pSource,   
        
        //-----------------------------------------------------------------------------
        // Renommage des colonnes et appel de la fonction pour la gestion de la casse
        //-----------------------------------------------------------------------------
        TransformSource = Table.RenameColumns(pTable
                        ,List.Transform(Table.ColumnNames(pTable),
                                        //-------------------------------------------------------------------
                                        // Appel de la fonction et passage en paramètre le nom de la colonne
                                        //-------------------------------------------------------------------
                                        each {_, FunctionReplaceCase(_)}
                                        )
                        )
    in
        TransformSource
in
    fnConvertTableColumnsNamesLa fonction interne est celle-ci
FunctionReplaceCase= (pElem as text) =>
let
ConvertCase=if Text.Upper(pCase) = "U" then
Text.Upper(pElem)
else if Text.Upper(pCase) = "L" then
Text.Lower(pElem)
else if Text.Upper(pCase) = "P" then
Text.Proper(pElem)
else null,
Replace=Text.Replace(ConvertCase, pStrFrom, pStrTo)
in
Replace,
Méthode 3 - elle donne les mêmes résultats mais elle est plus découpée en étapes afin d'avoir la main sur toutes les phases de transformation
		PowerQuery:
	
	
	//-------------------------------------------------------------------------------------------------------------------------
// Renommage des colonnes d'une table avec remplacement de caractères et modification de la casse
// Cette méthode a l'avantage de décomposer les étapes afin de les récupérer pour d'autres traitements au besoin
// Les étapes sont assez parlantes
//-------------------------------------------------------------------------------------------------------------------------
let fnConvertLUP = (pTable as table, pCase as text) as table =>
    let
        TableColumnNames =
            Table.ColumnNames(pTable),
        // Liste des colonnes à traiter
        ConvertListToTable =
            Table.FromList(
                TableColumnNames,
                Splitter.SplitByNothing(),
                null,
                null,
                ExtraValues.Error),
        // Fonction interne pour la gestion de la casse
        FunctionCase= (pElem as text) =>
            if Text.Upper(pCase) = "U" then
                Text.Upper(pElem)
                else if Text.Upper(pCase) = "L" then
                        Text.Lower(pElem)
                        else if Text.Upper(pCase) = "P" then
                                Text.Proper(pElem)
                                else null,
        
        // Ajout d'une colonne temporaire on en profite pour supprimer les espaces inutiles
        AddColumn =
            Table.AddColumn(
                ConvertListToTable,
                "__Case__",
                each Text.Trim(FunctionCase([Column1])
                    )
            ),
        
        // Création d'une liste temporaire pour le renommage
        ConvertToList =
            Table.AddColumn(
                AddColumn,
                "__ToList__",
                each Record.ToList(_)),
        
        // Passage en colonne dans la table résultat
        ConvertColumns =
            ConvertToList[__ToList__],
        
        // Renommage des colonnes
        RenameColumns =
            Table.RenameColumns(
                pTable,
                ConvertColumns)
    in
        RenameColumns
in
 fnConvertLUPPar cette variante on peut choisir le type de résultat à obtenir
		PowerQuery:
	
	
	//-------------------------------------------------------------------------------------------------------------------------
// Renommage des colonnes d'une table avec remplacement de caractères et modification de la casse
// Cette méthode a l'avantage de décomposer les étapes afin de les récupérer pour d'autres traitements au besoin
// Les étapes sont assez parlantes
//
// Là cette version on choisit le type de résultat en sortie dans la clause in
// - soit la table renommée
// - soit la table + la liste des colonnes
// - soit la liste des colonnes renommées de manière isolée
//-------------------------------------------------------------------------------------------------------------------------
let fnConvertLUP = (pTable as table, pCase as text, pOutput) as any =>
    let
        TableColumnNames =
            Table.ColumnNames(pTable),
        ConvertListToTable =
            Table.FromList(
                TableColumnNames,
                Splitter.SplitByNothing(),
                null,
                null,
                ExtraValues.Error),
        FunctionCase= (pElem as text) =>
            if Text.Upper(pCase) = "U" then
                Text.Upper(pElem)
                else if Text.Upper(pCase) = "L" then
                        Text.Lower(pElem)
                        else if Text.Upper(pCase) = "P" then
                                Text.Proper(pElem)
                                else null,
        
        AddColumn =
            Table.AddColumn(
                ConvertListToTable,
                "__Case__",
                each Text.Trim(FunctionCase([Column1])
                    )
            ),
        
        ConvertToList =
            Table.AddColumn(
                AddColumn,
                "__ToList__",
                each Record.ToList(_)),
        
        ConvertColumns =
            ConvertToList[__ToList__],
            
        RenameColumns =
            Table.RenameColumns(
                pTable,
                ConvertColumns)
    //-----------------------------------------------------
    // Sélection du résultat souhaité
    //-----------------------------------------------------
    in
        if Text.Upper(pOutput)="T" then
            RenameColumns
            else
                if Text.Upper(pOutput)="TL" then
                   ConvertToList
                   else if Text.Upper(pOutput)="L" then
                           ConvertColumns
                           else null
in
 fnConvertLUPMéthode 4 - ajout de préfixe ou de suffixe
		PowerQuery:
	
	
	//-------------------------------------------------------------------------------------------------------
// Fonction permettant de renommer des colonnes en suffixe ou préfixe
//-------------------------------------------------------------------------------------------------------
let fnConvertTableColumnsSP = (pSource as any,pSuffixPRefix as text, pDelim as text, pStr as text) as table =>   
    let
        pTable=if pSource is text then
                Excel.CurrentWorkbook(){[Name=pSource]}[Content]
                else pSource,
                        
        CaseSource = if Text.Upper(pSuffixPRefix) = "PREFIX" then
                        Table.TransformColumnNames(pTable,  each pStr & pDelim & _ )
                        else if Text.Upper(pSuffixPRefix) = "SUFFIX" then
                                Table.TransformColumnNames(pTable,  each _ & pDelim & pStr )
                                else pTable
    in
        CaseSource
in
    fnConvertTableColumnsSPUtilisation des fonctions
		PowerQuery:
	
	
	let
    Source = fnRenameAll1 (TB_Source,TB_RENOM)
in
    Source
let
    Source = fnRenameAll2 (TB_Source,TB_RENOM)
in
    Source
let
    Source = fnConvertColumnLUP1 (TB_Source," ", "_","U")
in
    Source
let
    Source = fnConvertColumnLUP2 (TB_Source," ", "$$","U")
in
    Source
let
    Source=fnConvertColumnLUP4(TB_Source,"P")
in
    Source
let
    Source=fnConvertColumnLUP5(TB_Source,"U","T")
in
    Source
let
    Source=fnConvertColumnLUP5(TB_Source,"U","l")
in
    Source
let
    Source=Table.RenameColumns(TB_Source,RenameAll_22_List)
in
    Source
let
    Source=TB_Source,
    LstColumnsConvert=fnConvertColumnLUP5(TB_Source,"U","l"),
    Convert=Table.RenameColumns(Source,LstColumnsConvert)
in
    Convert
let
    Source=TB_Source,
    LstColumnsConvert=fnConvertColumnLUP5(TB_Source,"U","l"),
    //fnConvertTableColumnsSP = (pSource as any,pSuffixPRefix as text, pDelim as text, pStr as text) 
    Convert=fnConvertColumnSP1(Source,"PREFIX","-","PREF")
in
    Convert
let
    Source=TB_Source,
    LstColumnsConvert=fnConvertColumnLUP5(TB_Source,"U","l"),
    //fnConvertTableColumnsSP = (pSource as any,pSuffixPRefix as text, pDelim as text, pStr as text) 
    Convert=fnConvertColumnSP1(Source,"SUFFIX","-","SUF")
in
    Convert