oguruma
XLDnaute Occasionnel
Ce post a pour but de démontrer qu'il est possible de réaliser des boucles de traitements au même titre qu'une boucle For Next en VBA.
Ces boucles permettent de générer des listes en tous genres, et transformer au besoin ces listes en tables.
Pour être sur le même que le Post sur les dates, List.Generate permet de générer des listes de jours, mois, années, etc..
Encore une fois cela montre qu'il n'y a pas de solutions universelles.
L'intérêt de ce post et surtout de montrer comment on utilise List.Generate qui fait peut-être partie des instructions les plus compliquées du langage M au même titre que List.Accumulate permettant de boucler sur des listes.
Le fichier joint montre ces exemples plus d'autres.
Boucle simplifiée
Cet exemple très simpliste démontre comment il est possible de réaliser une boucle imbriquée comme on avait deux for next
Boucle décroissante comme en VBA
Autre possibilité ajouter un traitement conditionnel dans la boucle
Indice et colonne - traitements non significatifs mis à titre d'exemple
Générer des mois
Générer des jours et jongler avec les mois
et un exemple de calendrier très simplifié
Ces boucles permettent de générer des listes en tous genres, et transformer au besoin ces listes en tables.
Pour être sur le même que le Post sur les dates, List.Generate permet de générer des listes de jours, mois, années, etc..
Encore une fois cela montre qu'il n'y a pas de solutions universelles.
L'intérêt de ce post et surtout de montrer comment on utilise List.Generate qui fait peut-être partie des instructions les plus compliquées du langage M au même titre que List.Accumulate permettant de boucler sur des listes.
Le fichier joint montre ces exemples plus d'autres.
Boucle simplifiée
PowerQuery:
let
//--------------------------------------------------------------------------------------------------------------
// Equivalent en VBA ==>
// For I=1 to 10
// debug.print I
// Next I
//--------------------------------------------------------------------------------------------------------------
// On obtient une liste de 1..10
//--------------------------------------------------------------------------------------------------------------
Source = List.Generate(
() => 1, // Valeur de départ elle s'initialise via une fonction représentée par le signe =>
each _ <= 10, // Condition de fin de boucle ; le signe '-' représente la valeur courante
each _ + 1 // Traitement on ajoute 1 à la valeur courante - Correspond au Next I en VBA avec traitement
)a
in
Source
PowerQuery:
let
[B]Ici dans cette boucle on simule en fait un indice via un nom de colonne nomme [i] dans l'exemple[/B]
//--------------------------------------------------------------------------------------------------------------
// Equivalent en VBA ==>
// For I=1 to 10
// debug.print I
// Next I
//--------------------------------------------------------------------------------------------------------------
// On obtient une liste de 1..10 via un nom de colonne considéré comme indice
//--------------------------------------------------------------------------------------------------------------
Source = List.Generate(
() => [i=0],
each [i] < 10,
each [ i = [i] + 1 ],
each [i]
)
in
Source
Cet exemple très simpliste démontre comment il est possible de réaliser une boucle imbriquée comme on avait deux for next
PowerQuery:
let
//--------------------------------------------------------------------------------------------------------------
// Equivalent en VBA ==>
// For I=1 to 10
// debug.print I
// Next I
//--------------------------------------------------------------------------------------------------------------
// On obtient une liste de 1..10 via un nom de colonne considéré comme indice
// et boucle imbriquée de 1 à 5
//--------------------------------------------------------------------------------------------------------------
Source = List.Generate(
()=>[i=1, j=1, z=0],
each [i] <= 10,
each
if [j] <= 5 then
[i=[i], j=[j]+1, z=[i] * [j]]
else
[i=[i]+1, j=1, z=0],
each [[i], [j], [z]]
),
ToRecord = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
ToTable = Table.ExpandRecordColumn(ToRecord, "Column1", {"i", "j", "z"}, {"Indice I", "Indice J", "Z"})
in
ToTable
Boucle décroissante comme en VBA
PowerQuery:
let
//--------------------------------------------------------------------------------------------------------------
// Equivalent en VBA ==>
// For I=10 to 1
// debug.print I
// Next I
//--------------------------------------------------------------------------------------------------------------
// On obtient une liste de 10..1
//--------------------------------------------------------------------------------------------------------------
Source = List.Generate(
() => 10, // Valeur de départ elle s'initialise via une fonction représentée par le signe =>
each _ > 0, // Condition de fin de boucle ; le signe '-' représente la valeur courante
each _ - 1 // Traitement on ajoute 1 à la valeur courante - Correspond au Next I en VBA avec traitement
)
in
Source
PowerQuery:
let
[B]Traitement avec création d'une liste personnalisée[/B]
//--------------------------------------------------------------------------------------------------------------
// Equivalent en VBA ==>
// For I=1 to 10
// debug.print "Elem n° " & I
// Next I
//--------------------------------------------------------------------------------------------------------------
Source = List.Generate(
() => 1, // Valeur de départ elle s'initialise via une fonction représentée par le signe =>
each _ <= 10, // Condition de fin de boucle ; le signe '-' représente la valeur courante
each _ + 1, // Traitement on ajoute 1 à la valeur courante - Correspond au Next I en VBA avec traitement
each "Elem n° " & Number.ToText(_) // On récupère le résultat pour le transformer
// Ici on concatene une chaine de caractères et une valeur que l'on doit convertir en texte
)
in
Source
Autre possibilité ajouter un traitement conditionnel dans la boucle
PowerQuery:
let
//--------------------------------------------------------------------------------------------------------------
// Equivalent en VBA ==>
// For I=1 to 10
// if I < 5 then debug.print "...." else debug.print "....."
// Next I
//--------------------------------------------------------------------------------------------------------------
Source = List.Generate(
() => 1, // Valeur de départ elle s'initialise via une fonction représentée par le signe =>
each _ <= 10, // Condition de fin de boucle ; le signe '-' représente la valeur courante
each _ + 1, // Traitement on ajoute 1 à la valeur courante - Correspond au Next I en VBA avec traitement
each if _ < 5 then "Note égale à " & Number.ToText(_) & " éliminatoire"
else "Note égale à " & Number.ToText(_) & " admis"
// On peut récupérer le résultat et appliquer un traitement conditionnel
)
in
Source
Indice et colonne - traitements non significatifs mis à titre d'exemple
PowerQuery:
let
Source = List.Generate(
() => [x = 1, y = 1, z = 0 ] ,
each [x] < 100 ,
each [y = [x] + [y],
x = [x] +1 ,
z = [y] ]
// each [z] * 2
),
ToRecord = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
ToTable = Table.ExpandRecordColumn(ToRecord, "Column1", {"x", "y", "z"}, {"X", "Y", "Z"})
in
ToTable
PowerQuery:
let
Source = List.Generate(
() => [x = 1, y = 1, z = 0 ] ,
each [x] < 100 ,
each [y = [x] + [y],
x = [x] +1 ,
z = [y] ] ,
each "[x] = " & Number.ToText([x]) & " [Y] = " & Number.ToText([y]) & " [Z] = " & Number.ToText([z])
)
in
Source
Générer des mois
PowerQuery:
let
ListMois = List.Generate(
() => 28, // Seuil pour passer au mois suivant on se base sur février
each _ < 364, // Limite pour s'arrêter en fin d'année
each _ + 28 , // Pour passer au mois suivant
each Text.Proper(Date.MonthName( _ )) & " " & Number.ToText(Date.Year(DateTime.LocalNow()),"####")
)
in
ListMois
PowerQuery:
let
YYYY=Date.Year(DateTime.LocalNow()),
YYYY10 = YYYY + 10,
ListMois = List.Generate(
() => [aa = #date(YYYY,1,1)],
each Date.Year([aa]) < YYYY10,
each [ aa = Date.AddMonths([aa],1)],
each Text.Proper(Date.MonthName( [aa] )) & "-" & Number.ToText(Date.Year([aa]))
)
in
ListMois
Générer des jours et jongler avec les mois
PowerQuery:
let
YYYY_DEBUT=Date.Year(DateTime.LocalNow()),
YYYY_FIN = YYYY_DEBUT + 5,
//-----------------------------------------------------------------------------------------------
// On va boucler sur les nn années calculées
//-----------------------------------------------------------------------------------------------
ListMois = List.Generate(
// Initialisation de la boucle sur les années avec le constructeur de dates
//-------------------------------------------------------------------------
() => [aa = #date(YYYY_DEBUT,1,1)],
each Date.Year([aa]) < YYYY_FIN,
// On ajoute une journée
//----------------------
each [ aa = Date.AddDays([aa],1)],
// Restitution du résultat avec la date
//-------------------------------------
each Text.Proper(Date.DayOfWeekName( [aa] )) & "-" &
Number.ToText(Date.Day([aa])) & "-" &
Text.Proper(Date.MonthName( [aa] )) & "-" &
Number.ToText(Date.Year([aa]))
)
in
ListMois
et un exemple de calendrier très simplifié
PowerQuery:
let
//-----------------------------------------------------------------------------------------------
// Démonstration d'un mini calendrier au travers d'une boucle
//-----------------------------------------------------------------------------------------------
YYYY_DEBUT=Date.Year(DateTime.LocalNow()),
YYYY_FIN = YYYY_DEBUT + 3,
//-----------------------------------------------------------------------------------------------
// On va boucler sur les nn années calculées
//-----------------------------------------------------------------------------------------------
ListMois = List.Generate(
//-------------------------------------------------------------------------
// Initialisation de la boucle sur les années avec le constructeur de dates
// Définiton de l'enregistrement date
//-------------------------------------------------------------------------
// Initialisation pour le 1er passage de la boucle
//-------------------------------------------------------------------------
() => [
aa = #date(YYYY_DEBUT,1,1),
jj=Date.Day(aa),
jjjj=Date.DayOfWeekName(aa),
mm=Date.Month(aa),
mmm=Date.MonthName(aa)
],
//-------------------------------
// Condition de fin de boucle
//-------------------------------
each Date.Year([aa]) - 1 < YYYY_FIN,
//----------------------
// On ajoute une journée
//----------------------
each [
aa = Date.AddDays([aa],1),
jj = Date.Day([aa]),
jjjj= Date.DayOfWeekName([aa]),
mm = Date.Month([aa]),
mmm = Date.MonthName([aa])
],
each [[aa], [jjjj], [jj], [mm], [mmm]]
),
ToTableFrom = Table.FromList(ListMois, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
ToTable = Table.ExpandRecordColumn(ToTableFrom, "Column1", {"aa", "jjjj", "jj", "mm", "mmm"}, {"aa", "jjjj", "jj", "mm", "mmm"})
in
ToTable