From 52456a3044005f40f69a2901d6640661917afd6d Mon Sep 17 00:00:00 2001 From: Alexis Olson Date: Sun, 14 Apr 2024 13:43:46 -0500 Subject: [PATCH 1/5] Add Text.ReplaceMany --- Functions/Text/Text.ReplaceMany.pq | 34 ++++++++++++++++++++++++++++++ M.pq | 1 + M_Creator.py | 1 + 3 files changed, 36 insertions(+) create mode 100644 Functions/Text/Text.ReplaceMany.pq diff --git a/Functions/Text/Text.ReplaceMany.pq b/Functions/Text/Text.ReplaceMany.pq new file mode 100644 index 0000000..e03ba81 --- /dev/null +++ b/Functions/Text/Text.ReplaceMany.pq @@ -0,0 +1,34 @@ +let + // Define metadata for the function, describing its purpose and usage. + metaDocumentation = type function ( + txt as (type text meta [ + Documentation.FieldCaption = "Text to modify" + ]), + replacements as (type {text} meta [ + Documentation.FieldCaption = "List of {old, new} text pairs" + ]) + ) as text meta [ + Documentation.Name = "Text.ReplaceMany", + Documentation.Author = "Alexis Olson", + Documentation.LongDescription = + // This is the description of the documentation, it only accepts a handful of HTML tags for formatting. + " + Performs the given replacements given by the list parameter in that order. +

The replacement list must contain text pairs where each pair is an old value and new value.

+
  • Author: Alexis Olson
  • +
  • LinkedIn: https://www.linkedin.com/in/alexis-olson-81726818/
  • + ", + Documentation.Examples = { + [ + Description = " Make three substitutions: š -> sh, Č -> Ch, and ž -> zh ", + Code = "Text.ReplaceMany(""Boštjan Černež"", {{""š"", ""sh""}, {""Č"", ""Ch""}, {""ž"", ""zh""}})", + Result = "Boshtjan Chernezh" + ] + } + ], + myFunction = + (txt as text, replacements as list) as text => + List.Accumulate(replacements, txt, (s, c) => Text.Replace(s, c{0}, c{1})) +in + // Apply the function metadata to myFunction. + Value.ReplaceType(myFunction, metaDocumentation) \ No newline at end of file diff --git a/M.pq b/M.pq index 153db11..0a79d42 100644 --- a/M.pq +++ b/M.pq @@ -98,6 +98,7 @@ let Json.Document = Json.Document, Json.FromValue = Json.FromValue, Lines.FromBinary = Lines.FromBinary, + List.Accumulate = List.Accumulate, List.Combine = List.Combine, List.Dates = List.Dates, List.IsEmpty = List.IsEmpty, diff --git a/M_Creator.py b/M_Creator.py index d0400b6..27e49df 100644 --- a/M_Creator.py +++ b/M_Creator.py @@ -23,6 +23,7 @@ 'microsoft.com', 'odata.nextLink', 'Table.ToM', + 'Text.ReplaceMany' 'Web.Contents', #Unfortunately adding this function to the M code will create a dynamic error :( 'www.linkedin', 'youtu.be', From b5f30260f12b68d0c6eb5f919811838e5bbb0f12 Mon Sep 17 00:00:00 2001 From: Alexis Olson Date: Sun, 14 Apr 2024 15:25:17 -0500 Subject: [PATCH 2/5] Draft Text.{Collapse, ContainsAll, ContainsAny} --- Functions/Text/Text.Collapse.pq | 48 ++++++++++++++++++++++++++++++ Functions/Text/Text.ContainsAllpq | 41 +++++++++++++++++++++++++ Functions/Text/Text.ContainsAny.pq | 41 +++++++++++++++++++++++++ M.pq | 4 +++ M_Creator.py | 5 +++- 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 Functions/Text/Text.Collapse.pq create mode 100644 Functions/Text/Text.ContainsAllpq create mode 100644 Functions/Text/Text.ContainsAny.pq diff --git a/Functions/Text/Text.Collapse.pq b/Functions/Text/Text.Collapse.pq new file mode 100644 index 0000000..2def057 --- /dev/null +++ b/Functions/Text/Text.Collapse.pq @@ -0,0 +1,48 @@ +let + // Define metadata for the function, describing its purpose and usage. + metaDocumentation = type function ( + txt as (type text meta [ + Documentation.FieldCaption = "The text to search" + ]), + characters as (type text meta [ + Documentation.FieldCaption = "Character(s) to collapse" + ]), + optional seperator as (type text meta [ + Documentation.FieldCaption = "Seperator to use in final result" + ]) + ) as text meta [ + Documentation.Name = "Text.Collapse", + Documentation.Author = "Alexis Olson", + Documentation.LongDescription = + // This is the description of the documentation, it only accepts a handful of HTML tags for formatting. + " + Collapses multiple characters into a single seperator +

    TODO

    +
  • Author: Alexis Olson
  • +
  • LinkedIn: https://www.linkedin.com/in/alexis-olson-81726818/
  • + ", + Documentation.Examples = { + [ + Description = " Simple usage is like an inner and outer trim. ", + Code = " Text.Collapse(""--a-b--c---d---"", ""-"") ", + Result = " a-b-c-d " + ], + [ + Description = " Multiple characters can be collapsed and the result combined using a different separator. ", + Code = " Text.Collapse(""[(a-..-b).--.((c-..-d))]"", ""[(-.)]"", "" <=> "") ", + Result = " a <=> b <=> c <=> d " + ] + } + ], + myFunction = + (txt as text, characters as text, optional seperator as text) as text => + let + Split = Text.SplitAny(txt, characters), + RemoveEmpty = List.Select(Split, each _ <> null and _ <> ""), + Seperator = seperator ?? Text.Start(characters, 1), + Result = Text.Combine(RemoveEmpty, Seperator) + in + Result +in + // Apply the function metadata to myFunction. + Value.ReplaceType(myFunction, metaDocumentation) \ No newline at end of file diff --git a/Functions/Text/Text.ContainsAllpq b/Functions/Text/Text.ContainsAllpq new file mode 100644 index 0000000..637d3b3 --- /dev/null +++ b/Functions/Text/Text.ContainsAllpq @@ -0,0 +1,41 @@ +let + // Define metadata for the function, describing its purpose and usage. + metaDocumentation = type function ( + txt as (type text meta [ + Documentation.FieldCaption = "The text to search" + ]), + substrings as (type {text} meta [ + Documentation.FieldCaption = "A list of substrings to search for" + ]), + optional comparer as (type function meta [ + Documentation.FieldCaption = "Comparer function to pass into Text.Contains" + ]) + ) as logical meta [ + Documentation.Name = "Text.ContainsAll", + Documentation.Author = "Alexis Olson", + Documentation.LongDescription = + // This is the description of the documentation, it only accepts a handful of HTML tags for formatting. + " + Checks whether the input text contains all of the substrings from the input list. +

    Returns true if all of the substrings are found.

    +
  • Author: Alexis Olson
  • +
  • LinkedIn: https://www.linkedin.com/in/alexis-olson-81726818/
  • + ", + Documentation.Examples = { + [ + Description = " TODO ", + Code = " TODO ", + Result = " TODO " + ] + } + ], + myFunction = + (txt as text, substrings as list, optional comparer as function) as logical => + let + Matches = List.Select(substrings, (substr) => Text.Contains(txt, substr, comparer)), + Result = (substrings = Matches) // + in + Result +in + // Apply the function metadata to myFunction. + Value.ReplaceType(myFunction, metaDocumentation) \ No newline at end of file diff --git a/Functions/Text/Text.ContainsAny.pq b/Functions/Text/Text.ContainsAny.pq new file mode 100644 index 0000000..1708b62 --- /dev/null +++ b/Functions/Text/Text.ContainsAny.pq @@ -0,0 +1,41 @@ +let + // Define metadata for the function, describing its purpose and usage. + metaDocumentation = type function ( + txt as (type text meta [ + Documentation.FieldCaption = "The text to search" + ]), + substrings as (type {text} meta [ + Documentation.FieldCaption = "A list of substrings to search for" + ]), + optional comparer as (type function meta [ + Documentation.FieldCaption = "Comparer function to pass into Text.Contains" + ]) + ) as logical meta [ + Documentation.Name = "Text.ContainsAny", + Documentation.Author = "Alexis Olson", + Documentation.LongDescription = + // This is the description of the documentation, it only accepts a handful of HTML tags for formatting. + " + Checks whether the input text contains any of the substrings from the input list. +

    Returns true if any of the substrings are found.

    +
  • Author: Alexis Olson
  • +
  • LinkedIn: https://www.linkedin.com/in/alexis-olson-81726818/
  • + ", + Documentation.Examples = { + [ + Description = " TODO ", + Code = " TODO ", + Result = " TODO " + ] + } + ], + myFunction = + (txt as text, substrings as list, optional comparer as function) as logical => + let + Matches = List.Select(substrings, (substr) => Text.Contains(txt, substr, comparer)), + Result = not List.IsEmpty(Matches) + in + Result +in + // Apply the function metadata to myFunction. + Value.ReplaceType(myFunction, metaDocumentation) \ No newline at end of file diff --git a/M.pq b/M.pq index 0a79d42..55cc254 100644 --- a/M.pq +++ b/M.pq @@ -104,6 +104,7 @@ let List.IsEmpty = List.IsEmpty, List.MatchesAny = List.MatchesAny, List.RemoveItems = List.RemoveItems, + List.Select = List.Select, List.Transform = List.Transform, List.Type = List.Type, Logical.Type = Logical.Type, @@ -125,9 +126,12 @@ let Table.TransformColumns = Table.TransformColumns, Text.BeforeDelimiter = Text.BeforeDelimiter, Text.Combine = Text.Combine, + Text.Contains = Text.Contains, Text.From = Text.From, Text.Repeat = Text.Repeat, Text.Replace = Text.Replace, + Text.SplitAny = Text.SplitAny, + Text.Start = Text.Start, Text.ToBinary = Text.ToBinary, Text.Type = Text.Type, Time.Hour = Time.Hour, diff --git a/M_Creator.py b/M_Creator.py index 27e49df..1832393 100644 --- a/M_Creator.py +++ b/M_Creator.py @@ -23,7 +23,10 @@ 'microsoft.com', 'odata.nextLink', 'Table.ToM', - 'Text.ReplaceMany' + 'Text.Collapse', + 'Text.ContainsAll', + 'Text.ContainsAny', + 'Text.ReplaceMany', 'Web.Contents', #Unfortunately adding this function to the M code will create a dynamic error :( 'www.linkedin', 'youtu.be', From bd5d6be2ec597d15fec96448e2665d8288b14b7a Mon Sep 17 00:00:00 2001 From: Alexis Olson Date: Mon, 15 Apr 2024 18:20:49 -0500 Subject: [PATCH 3/5] Update text function documentation --- Functions/Text/Text.Collapse.pq | 10 +++++----- Functions/Text/Text.ContainsAllpq | 18 ++++++++++++++---- Functions/Text/Text.ContainsAny.pq | 18 ++++++++++++++---- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/Functions/Text/Text.Collapse.pq b/Functions/Text/Text.Collapse.pq index 2def057..96e4a13 100644 --- a/Functions/Text/Text.Collapse.pq +++ b/Functions/Text/Text.Collapse.pq @@ -16,20 +16,20 @@ let Documentation.LongDescription = // This is the description of the documentation, it only accepts a handful of HTML tags for formatting. " - Collapses multiple characters into a single seperator -

    TODO

    + Collapses multiple characters into a single seperator. +

    See the illustrative examples listed below.

  • Author: Alexis Olson
  • LinkedIn: https://www.linkedin.com/in/alexis-olson-81726818/
  • ", Documentation.Examples = { [ - Description = " Simple usage is like an inner and outer trim. ", - Code = " Text.Collapse(""--a-b--c---d---"", ""-"") ", + Description = " Simple usage is similar to an inner and outer trim. ", + Code = " Text.Collapse(""--a----b--c---d---"", ""-"") ", Result = " a-b-c-d " ], [ Description = " Multiple characters can be collapsed and the result combined using a different separator. ", - Code = " Text.Collapse(""[(a-..-b).--.((c-..-d))]"", ""[(-.)]"", "" <=> "") ", + Code = " Text.Collapse(""[(a-..-b).-.-.-.((c-..-d))]"", ""[(-.)]"", "" <=> "") ", Result = " a <=> b <=> c <=> d " ] } diff --git a/Functions/Text/Text.ContainsAllpq b/Functions/Text/Text.ContainsAllpq index 637d3b3..70eff62 100644 --- a/Functions/Text/Text.ContainsAllpq +++ b/Functions/Text/Text.ContainsAllpq @@ -23,9 +23,19 @@ let ", Documentation.Examples = { [ - Description = " TODO ", - Code = " TODO ", - Result = " TODO " + Description = " Check if a sentence is a pangram (contains all letters of the alphabet). ", + Code = " Text.ContainsAll(""The quick brown fox jumps over the lazy dog."", {""a""..""z""}) ", + Result = " TRUE " + ], + [ + Description = " By default, comparison is case-sensitive and the following has an S but no s. ", + Code = " Text.ContainsAny(""Sphinx of black quartz, judge my vow."", {""a""..""z""}) ", + Result = " FALSE " + ], + [ + Description = " Comparison can be done using a case-insenitive comparer. ", + Code = " Text.ContainsAny(""Sphinx of black quartz, judge my vow."", {""a""..""z""}, Comparer.OrdinalIgnorCase ) ", + Result = " TRUE " ] } ], @@ -33,7 +43,7 @@ let (txt as text, substrings as list, optional comparer as function) as logical => let Matches = List.Select(substrings, (substr) => Text.Contains(txt, substr, comparer)), - Result = (substrings = Matches) // + Result = (substrings = Matches) // All substrings are matched in Result in diff --git a/Functions/Text/Text.ContainsAny.pq b/Functions/Text/Text.ContainsAny.pq index 1708b62..2d3226f 100644 --- a/Functions/Text/Text.ContainsAny.pq +++ b/Functions/Text/Text.ContainsAny.pq @@ -23,9 +23,19 @@ let ", Documentation.Examples = { [ - Description = " TODO ", - Code = " TODO ", - Result = " TODO " + Description = " Check if a sentence is a pangram. ", + Code = " Text.ContainsAll(""Hello World!"", {""and"", ""or""}) ", + Result = " TRUE " + ], + [ + Description = " By default, comparison is case-sensitive. ", + Code = " Text.ContainsAny(""Hello World!"", {""hello"", ""world""}) ", + Result = " FALSE " + ], + [ + Description = " Comparison can be done using a case-insenitive comparer. ", + Code = " Text.ContainsAny(""Hello World!"", {""hello"", ""kitty""}, Comparer.OrdinalIgnorCase ) ", + Result = " TRUE " ] } ], @@ -33,7 +43,7 @@ let (txt as text, substrings as list, optional comparer as function) as logical => let Matches = List.Select(substrings, (substr) => Text.Contains(txt, substr, comparer)), - Result = not List.IsEmpty(Matches) + Result = not List.IsEmpty(Matches) // At least one substring is matched in Result in From c8eefaf5f0449acef285906dcf194e28c8c697bb Mon Sep 17 00:00:00 2001 From: Alexis Olson Date: Mon, 15 Apr 2024 18:29:15 -0500 Subject: [PATCH 4/5] Fix typo --- Functions/Text/Text.ContainsAny.pq | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Functions/Text/Text.ContainsAny.pq b/Functions/Text/Text.ContainsAny.pq index 2d3226f..7b156e6 100644 --- a/Functions/Text/Text.ContainsAny.pq +++ b/Functions/Text/Text.ContainsAny.pq @@ -34,7 +34,7 @@ let ], [ Description = " Comparison can be done using a case-insenitive comparer. ", - Code = " Text.ContainsAny(""Hello World!"", {""hello"", ""kitty""}, Comparer.OrdinalIgnorCase ) ", + Code = " Text.ContainsAny(""Hello World!"", {""hello"", ""kitty""}, Comparer.OrdinalIgnoreCase ) ", Result = " TRUE " ] } From f8948b514589a65832dc30a67d5fdd540ff08dfb Mon Sep 17 00:00:00 2001 From: Alexis Olson Date: Mon, 15 Apr 2024 18:32:56 -0500 Subject: [PATCH 5/5] Fix extension --- Functions/Text/{Text.ContainsAllpq => Text.ContainsAll.pq} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Functions/Text/{Text.ContainsAllpq => Text.ContainsAll.pq} (97%) diff --git a/Functions/Text/Text.ContainsAllpq b/Functions/Text/Text.ContainsAll.pq similarity index 97% rename from Functions/Text/Text.ContainsAllpq rename to Functions/Text/Text.ContainsAll.pq index 70eff62..608734c 100644 --- a/Functions/Text/Text.ContainsAllpq +++ b/Functions/Text/Text.ContainsAll.pq @@ -34,7 +34,7 @@ let ], [ Description = " Comparison can be done using a case-insenitive comparer. ", - Code = " Text.ContainsAny(""Sphinx of black quartz, judge my vow."", {""a""..""z""}, Comparer.OrdinalIgnorCase ) ", + Code = " Text.ContainsAny(""Sphinx of black quartz, judge my vow."", {""a""..""z""}, Comparer.OrdinalIgnoreCase ) ", Result = " TRUE " ] }