4 | Valeurs et expressions
En bref
- Objectifs : distinguer valeurs primitives et valeurs structurées ; écrire et manipuler listes, enregistrements et tables ; accéder à une ligne, une colonne ou une cellule ; gérer les erreurs avec try, otherwise et error.
- Prérequis : chapitres 1 à 3 de la série.
- Niveau : avancé.
Pourquoi ?
Le code M manipule en permanence des listes (les valeurs d'une colonne), des enregistrements (une ligne) et des tables. Savoir les écrire et y accéder permet de lire une valeur précise d'une autre requête, de tester une transformation sur une petite table saisie dans le code, ou de remplacer une valeur fixe par une valeur calculée.
Notions clés
Les catégories de valeurs
- Valeurs primitives : une donnée élémentaire (nombre, texte, date...).
- Valeurs structurées : elles contiennent d'autres valeurs. Il en existe trois : les listes, les enregistrements et les tables.
- Fonctions : en M, une fonction est une valeur comme une autre (chapitre « Techniques avancées en M »).
- Types : des valeurs qui décrivent le type d'autres valeurs (chapitre 5).
| Catégorie | Type | Exemple de littéral |
| Primitive | Null | null |
| Primitive | Logique | true, false |
| Primitive | Nombre | 0, 5, -5, 1.5 |
| Primitive | Heure | #time(20, 15, 30) |
| Primitive | Date | #date(2024, 3, 1) |
| Primitive | Date et heure | #datetime(2023, 5, 10, 20, 15, 30) |
| Primitive | Date, heure et fuseau | #datetimezone(2023, 5, 10, 20, 15, 30, 9, 0) |
| Primitive | Durée | #duration(5, 2, 15, 0) (5 jours, 2 heures, 15 minutes) |
| Primitive | Texte | "bonjour" |
| Primitive | Binaire | #binary("AQID") |
| Structurée | Liste | {1, 2, 3} |
| Structurée | Enregistrement | [A = 1, B = 2] |
| Structurée | Table | #table({"X", "Y"}, {{0, 1}, {1, 0}}) |
| Fonction | Fonction | (x) => x + 1 |
| Type | Type | type text, type table [A = any, B = text] |
Listes
Une liste s'écrit entre accolades ; elle peut contenir des valeurs de toute nature :
{"a", {4, 5, 6}, [Column1 = 1, Column2 = "v"], #table({"ID", "Produit"}, {{1, "Pomme"}})}List.Sort(List.Combine({{1, 4}, {2, 5}})) // renvoie {1, 2, 4, 5}
List.Numbers(1, 5, 1) // renvoie {1, 2, 3, 4, 5}
{1..5} // renvoie aussi {1, 2, 3, 4, 5}L'opérateur .. crée une plage, y compris de caractères, et & concatène deux listes :
let
Lettres = {"a".."z"},
Chiffres = {"0".."9"},
Autorises = Lettres & Chiffres,
Resultat = Text.Select("inv-1006-!**!-(act)", Autorises)
in
ResultatRésultat : « inv1006act ». Le n-ième élément s'obtient par Liste{n}, en commençant à 0 : {"a", "b", "c"}{1} renvoie « b ».
Enregistrements
Un enregistrement est un ensemble de champs nommés, entre crochets ; c'est la forme d'une ligne de table.
let
Client = [Nom = "Durand", Ville = "Lyon", CA = 12500],
Ville = Client[Ville], // "Lyon"
Region = Client[Region]?, // null au lieu d'une erreur
Champ = Record.Field(Client, "CA") // 12500, nom du champ passé en texte
in
ChampRecord.Field permet de choisir le champ par son nom calculé, à la manière de la fonction INDIRECT d'Excel. Dans une colonne personnalisée, Record.Field(_, [Champ]) lit la colonne dont le nom figure dans la colonne Champ. Source
Tables et accès aux valeurs
| Syntaxe | Résultat |
Table[Colonne] | La colonne, sous forme de liste |
Table{0} | La première ligne, sous forme d'enregistrement |
Table{0}[Colonne] | La valeur de la première ligne pour cette colonne |
Table{[Item = "Ventes", Kind = "Sheet"]} | La ligne qui correspond aux valeurs indiquées (utilisé par l'étape Navigation) |
Exemple : remplacer dans une colonne la valeur de la première ligne, quelle qu'elle soit, au lieu d'un texte fixe :
= Table.ReplaceValue(#"Type modifié", #"Type modifié"{0}[Colonne 1], "test", Replacer.ReplaceValue, {"Colonne 1"})Table.Max(Table, "Colonne") renvoie la ligne qui a la valeur maximale ; Table.Max(TypeModifie, "Colonne1")[Colonne1] en extrait la valeur.
Opérateurs

- Arithmétiques :
+,-,*,/; comparaison :=,<>,<,<=,>,>=; logiques :and,or,not. &concatène deux textes, deux listes, deux enregistrements ou deux tables.??renvoie la valeur de droite si celle de gauche estnull:[Remise] ?? 0.+ne concatène pas du texte :"A" + "B"provoque une erreur,"A" & "B"renvoie « AB ». Source
Gestion des erreurs
try Number.From([Montant]) otherwise 0 // 0 si la conversion échoue
try [Montant] // enregistrement HasError, Value ou Error
error "Code client manquant" // déclenche une erreur personnaliséetry renvoie un enregistrement : HasError vaut true en cas d'erreur, et le champ Error décrit l'erreur (Reason, Message, Detail). Source
Exemple
Table de test à coller dans une requête vide : quatre colonnes mêlant nombres, textes et null, puis typées en nombre entier. Les cellules non convertibles deviennent des erreurs de cellule, à traiter avec les techniques de la page « Profiler et nettoyer les données ».
let
Source = Table.FromColumns({
{123, 34, 12, "txt", null},
{123, "txt", 34, 12, null},
{"txt", 123, 34, 12, null},
{123, "txt", "34", 12, null}
}),
TypeModifie = Table.TransformColumnTypes(Source, {
{"Column1", Int64.Type}, {"Column2", Int64.Type},
{"Column3", Int64.Type}, {"Column4", Int64.Type}
}),
SansErreurs = Table.ReplaceErrorValues(TypeModifie, {
{"Column1", null}, {"Column2", null}, {"Column3", null}, {"Column4", null}
})
in
SansErreursObservez l'étape TypeModifie : chaque « txt » devient Error, alors que le texte « 34 » de Column4 est converti en nombre. L'étape SansErreurs remplace les erreurs par null.
[Capture : aperçu de l'étape TypeModifie, cellules Error dans les quatre colonnes]
Pièges et bonnes pratiques
- Les positions commencent à 0 :
Table{0}est la première ligne. Table{0}[Colonne]dépend de l'ordre des lignes : triez ou filtrez avant de lire une valeur par sa position.- Préférez
try ... otherwiseà la suppression des erreurs quand une valeur de repli a un sens métier.
À retenir
- Trois valeurs structurées : listes
{}, enregistrements[], tables. Table[Colonne]donne une liste,Table{n}une ligne,Table{n}[Colonne]une valeur.&concatène,??remplace unnull.try ... otherwisefournit une valeur de repli,errordéclenche une erreur.