Lambda Expressions in Power Query - Teil 2 - Kontext Interaktion

Von Lukas Hillesheim, 30. August 2022
Blog Article & Copyright by Lukas Hillesheim
In Teil 1 dieser Serie wurde darauf hingewiesen, dass die MSDN keine Auskunft über den konkreten (Daten-) Verkehr zwischen der aufrufenden Funktion - meist ein Iterator - und der Lambda Expression gibt. Teil 2 dieser Serie zeigt an drei konkreten Beispielen, wie Power Query Iterator und Lambda Expression miteinander interagieren.
Bezüglich der Interaktion von Iterator und Lambda Expression tauchen folgende Fragen auf:
  • - Wie viele Variablen muss die Lambda Expression zur Verfügung stellen, um die Daten des Iterators entgegen zu nehmen (0, 1, n)
  • - Welche Daten-Typen werden vom Iterator an die Lambda Expression übergeben?
  • - Welcher Daten-Typ (Scalar, Array, Object etc.) muss von der Lambda Expression an den Iterator zurückgegeben werden?
  • - Was macht der Iterator mit dem Rückgabe-Wert der Lambda Expression (Business Logik des Iterators)?
  • - Welche Daten gibt der Iterator zurück?
Aufruf einer Lambda Expression innerhalb eines Iterators:
<iterator> ( ( <variable>, n ) => <lambda_expression> )
Für die Beispiele dieses Artikels werden diese drei Iteratoren verwendet:
  • - List.Transform
  • - Table.TransformRows
  • - Record.TransformFields

1. Allgemeines

MSDN Hilfe zu den drei Beispiel-Funktionen:
  • List.Transform (
  • list as list,
  • transform as function) as list
  • Table.TransformRows(
  • table as table,
  • transform as function) as list
  • Record.TransformFields(
  • record as record,
  • transformOperations as list) as record
Alle drei Iteratoren implementieren eine Art von ETL-Logik. "ETL" steht für
  • - E(xtract). Extraktion der Daten aus der Quelle
  • - T(ransform). Anwenden von Logik zur Modifikation der Daten. Ableiten von neuen Daten auf Grund der Quell-Daten
  • - L(oad). Ablegen der Daten in das Ziel
Die Lambda Expression ist für das "T", also den Transformations-Teil, verantwortlich.
Iteration. Alle drei Funktionen sind Iteratoren. Aus der OOP-Sicht implementieren sie alle eine Schleife:
  • - List.Transform(). Foreach ( item i in <list> ) { … }
  • - Table.TransformRows(). Foreach ( Row r in <table>.Rows ) { … }
  • - Record.TransformFields(). Foreach ( Field f in <record>.Fields) {...}
Ergebnis vom Typ “Iterable”. Alle drei Funktionen liefern ein Listen-Objekt zurück:
  • - List.Transform() as list
  • - Table.TransformRows() as list
  • - Record.TransformFields() as record. Eine Liste von Feldern
Argument vom Typ “function”. Alle drei Funktionen verfügen über ein Argument vom Typ "function":
  • - List.Transform ( … , transform as function ). Einzelne Funktion bzw. Lambda Expression, die eine Transformations-Aufgabe ausführt.
  • - Table.TransformRows ( … , transform as function ). Einzelne Funktion bzw. Lambda Expression, die eine Transformations-Aufgabe ausführt.
  • - Record.TransformFields ( …, transformOperations as list, … ). Mehrere Funktionen bzw. Lambda Expressions, die pro Feld eine Transformations-Aufgabe durchführen.

2. List.Transform

Beispiel 1 zu List.Transform.
Figure 1 - List.Transform with simple lambda expression
Figure 1 - List.Transform with simple lambda expression
Schritt 1 - Deklaration der Variablen "list0". Eine Liste von drei Nummern wird deklariert.
Schritt 2 - Deklaration der Variablen "list1". Auf der linken Seite: Jede Nummer der Liste wird sukzessive der Lambda Variablen zugewiesen. Auf der rechten Seite: die Lambda Expression multipliziert die Nummer mit 10. Das Ergebnis wird in das Result Set eingefügt.
Figure 2 - List.Transform with simple lambda expression.Result.
Figure 2 - List.Transform with simple lambda expression.Result.
Beispiel 2 zu List.Transform.
Figure 3 - List.Transform with a constant expression
Figure 3 - List.Transform with a constant expression
Die Eingabe ist eine Liste mit Nummern, die Schleife läuft drei Mal. In jedem Schleifen-Durchgang wird der konstante Wert "test" in das Result Set eingefügt.
Figure 4 - List.Transform with a constant expression. Result.
Figure 4 - List.Transform with a constant expression. Result.
Beispiel 3 zu List.Transform.
Figure 5 - List.Transform: result is a list of records.
Figure 5 - List.Transform: result is a list of records.
Die Eingabe besteht aus einer Liste von Nummern, das Ergebnis ist eine Liste von Records. Abbildung 7 zeigt Record 1
Figure 6 - List of records.
Figure 6 - List of records.
Figure 7 - Record 1.
Figure 7 - Record 1.
Beispiel 4 zu List.Transform.
Figure 8 - List.Transform: the input is a list of records.
Figure 8 - List.Transform: the input is a list of records.
Die Eingabe ist eine Liste von Records. Unter Berücksichtigung dieser Tatsache wird die Lambda Variable "record" genannt. Die Lambda Expression auf der rechten Seite gibt ebenfalls einen Wert vom Typ Record zurück. Beide Ausgabespalten, "Id" und "Name", leiten ihren Wert vom Eingabe-Record ab.
Figure 9 - List.Transform: the output is a list of records.
Figure 9 - List.Transform: the output is a list of records.
Figure 10 - Record 1.
Figure 10 - Record 1.
Beispiel 5 zu List.Transform.
Figure 11 - List.Transform: the input is a list of lists.
Figure 11 - List.Transform: the input is a list of lists.
Die Eingabe besteht aus einer Liste von Listen. Unter Berücksichtigung dieser Tatsache ist der Name "list" für die Lambda Variable vergeben. Die Lambda Expression auf der rechten Seite berechnet die Anzahl der Elemente für jede Liste. Das erste Element ist eine Liste mit Nummern zwischen 1 und 3, das zweite Element ist eine Liste mit Buchstaben von "A" bis "G" und das dritte Element ist eine Liste mit Datums-Werten zwischen 1.7.2022 bis 10.7.2022.
Figure 12 - List.Transform: the output is a list of scalar values each representing the count of list items.
Figure 12 - List.Transform: the output is a list of scalar values each representing the count of list items.

3. Table.TransformRows

Beispiel 1 zu Table.TransformRows
Figure 13 -Table.TransformRows: the input is a table with its rows.
Figure 13 - Table.TransformRows: the input is a table with its rows.
Die Eingabe, "table0", ist eine Tabelle mit drei Zeilen.
Achtung: In PowerQuery muss man zwischen "Zeile" und "Record" unterscheiden. Eine "Zeile" ist ein Element einer Tabelle. Ein "Record" ist ein von einer Tabelle unabhängiges Objekt. Es kann, muss aber nicht unbedingt auch Teil einer Liste sein. Der Unterschied zwischen den ähnlichen Objekten, "Zeile" und "Record", kommt zum Tragen, indem von allen Zeilen einer Tabelle verlangt wird, dass sie über das gleiche Schema verfügen; also gleiche Anzahl von Spalten und gleiche Typen. Von einer Record-Liste wird das nicht erwartet; jeder Record kann einem anderen Schema folgen.
Schritt 1 - Deklaration der Variablen "table0". Inhalt der Variablen:
Figure 14 -Table.TransformRows: content of variable ‘table0’
Figure 14 - Table.TransformRows: content of variable ‘table0’
Schritt 2 - Deklaration der Variablen "list0". Für jede Zeile der Eingabe wird ein neuer Record in die Ausgabe eingefügt. Das Ergebnis ist also eine Liste von Records:
Figure 15 - Table.TransformRows: the result is a list of records.
Figure 15 - Table.TransformRows: the result is a list of records.
Inhalt des ersten Records:
Figure 16 - Table.TransformRows: first record.
Figure 16 - Table.TransformRows: first record.
Schritt 3 - Deklaration der Variablen "table1". Eine Herausforderung bei der Verwendung von "Table.TransformRows" besteht darin, die Liste der Records in eine Tabelle zurück zu verwandeln. Diese Aufgabe wird mit "Table.FromRecords" gelöst: 
Figure 17 - Table.TransformRows: Re-convert record list into table.
Figure 17 - Table.TransformRows: Re-convert record list into table.

4. Record.TransformFields

Beispiel 1 zu Record.TransformFields
Figure 18 - Record.TransformFields: a list of lambda expressions is applied
Figure 18 - Record.TransformFields: a list of lambda expressions is applied
Schritt 1 - Deklaration der Variablen "record0". Ein Record mit vier Spalten wird deklariert.
Schritt 2 - Deklaration der Variablen "record1". Das zweite Argument für "Record.TransformFields" ist eine Liste von Lambda Expressions. Jede einzelne Lambda Expression ist dafür zuständig, für eine bestimmte Spalte eine Transformation durchzuführen. Die jeweilige Spalte wird "links" genannt. Im Beispiel sind es die Spalten "Amount" und "Date". Jede Spalte des Eingabe-Records, die nicht in der Liste der Transformationen erwähnt wird, wird in den Ergebnis-Record ohne Änderung übernommen. Dies trifft auf "CustId" und "CustName" zu.
Figure 19 - Record.TransformFields: 2 fields were transformed (Amount := amount gross, Date := due date).
Figure 19 - Record.TransformFields: 2 fields were transformed (Amount := amount gross, Date := due date).
Die Syntax-Hilfe der MSDN enthält ein Beispiel, in dem die Funktion "Record.TransformFields" benutzt wird, um die Typen von Spalten zu modifizieren:
Figure 20 - MSDN syntax help for 'Record.TransformFields'
Figure 20 - MSDN syntax help for 'Record.TransformFields'
Bezüglich des MSDN-Beispiels kann man sich vorstellen, dass der Ausdruck "Number.FromText" von der Power Query Engine zu folgender Lambda Expression expandiert wird:
(fieldValue) => Number.FromText (fieldValue)