Sometimes I need to retrieve the textual representation of a type in Power Query and I’m using a fairly verbose function that I’ve stolen ages ago (I believe it was here: https://ssbi-blog.de/technical-topics-english/power-query-editor-using-notepad/ ) :
let func = | |
(t as type) as text => | |
let | |
nonNullableType = Type.NonNullable(t), | |
TypeDescription = if Type.Is(nonNullableType, type binary) then "binary" | |
else if Type.Is(nonNullableType, type date) then "date" | |
else if Type.Is(nonNullableType, type datetime) then "datetime" | |
else if Type.Is(nonNullableType, type datetimezone) then "datetimezone" | |
else if Type.Is(nonNullableType, type duration) then "duration" | |
else if Type.Is(nonNullableType, type function) then "function" | |
else if Type.Is(nonNullableType, type list) then "list" | |
else if Type.Is(nonNullableType, type logical) then "logical" | |
else if Type.Is(nonNullableType, type none) then "none" | |
else if Type.Is(nonNullableType, type null) then "null" | |
else if Type.Is(nonNullableType, type number) then "number" | |
else if Type.Is(nonNullableType, type record) then "record" | |
else if Type.Is(nonNullableType, type table) then "table" | |
else if Type.Is(nonNullableType, type text) then "text" | |
else if Type.Is(nonNullableType, type time) then "time" | |
else if Type.Is(nonNullableType, type type) then "type" | |
else if Type.Is(nonNullableType, type action) then "action" | |
else if Type.Is(type anynonnull, nonNullableType) then "any" | |
else error "Unknown type", | |
TypeString = if TypeDescription = "any" then | |
if Type.IsNullable(t) then | |
"any" else "anynonnull" | |
else if Type.IsNullable(t) then | |
"nullable " & TypeDescription | |
else TypeDescription | |
in | |
TypeString , | |
documentation = [ | |
Documentation.Name = " Type.AsText ", | |
Documentation.Description = " Returns type in text format ", | |
Documentation.LongDescription = " Returns type in text format ", | |
Documentation.Category = " Type ", | |
Documentation.Source = " not sure ", | |
Documentation.Version = " 1.0 ", | |
Documentation.Author = " Imke Feldmann: www.TheBIccountant.com. https://www.thebiccountant.com/?p=3871 . ", | |
Documentation.Examples = {[Description = " ", | |
Code = " ", | |
Result = " "]}] | |
in | |
Value.ReplaceType(func, Value.ReplaceMetadata(Value.Type(func), documentation)) |
Although I don’t know how this function will be evaluated, I suspect it checks for every type from the beginning until the matching type is found. So I was tinkering with a potentially faster solution that is based on a merge of tables: Create a table with types:
and merge it on the type itself with the types in my original table:
let
Source = #shared,
Functions = Record.ToTable(Source),
#"Added Custom" = Table.AddColumn(Functions, "Type", each Value.Type([Value])),
#"Kept Last Rows" = Table.LastN(#"Added Custom", 1),
Types = Record.ToTable([table = type table, function = type function]),
#"Merged Queries" = Table.NestedJoin(#"Kept Last Rows",{"Type"},Types,{"Value"},"Types",JoinKind.LeftOuter),
#"Expanded Types" = Table.ExpandTableColumn(#"Merged Queries", "Types", {"Name"}, {"Name.1"})
in
#"Expanded Types"
But this function will not return any matches. I also tried out a (potentially) slower version using Table.SelectColumns(Types, each [Value] = x[Types]) – but still no match.
What I found particularly frustrating here was, that in some cases, these lookups or filters on type-columns worked. This for example:
let
Source = #shared,
Functions = Record.ToTable(Source),
#"Added Custom" = Table.AddColumn(Functions, "Type", each Value.Type([Value])),
#"Kept Last Rows" = Table.LastN(#"Added Custom", 1),
Types = Record.ToTable([table = type table, function = type function]),
#"Merged Queries" = Table.NestedJoin(Types, {"Value"}, Types, {"Value"}, "Types", JoinKind.LeftOuter),
#"Expanded Types" = Table.ExpandTableColumn(#"Merged Queries", "Types", {"Name"}, {"Name.1"})
in
#"Expanded Types"
As it turns out, M is not equipped to compare types: https://social.technet.microsoft.com/Forums/en-US/6f2f0336-e57e-477f-a40e-5ffc9f0ca7be/type-equivalence?forum=powerquery
“Type equivalence is not defined in M. Any two type values that are compared for equality may or may not return true.”
This is exactly what I was experiencing: Some comparisons worked and some didn’t.
Although this is not an everyday use case, I hope this blogpost will prevent you (and me) to run into this trap (again).
Enjoy & stay queryious 😉
Pingback: No Type Equivalence In M – Curated SQL