Forum Discussion

Dicken's avatar
Dicken
Icon for Post Prodigy rankPost Prodigy
6 days ago
Solved

Accumulation, result error / not error

Hello all,

 

I'd like a bit of help as to how this works, the below returns and error, but if does work in the end, think i know why, and which I have put below, so this returns a list of errors, I think the first logical test returns true / false to the second must return and error ?

let   atext     = "the cat's bed, the dogs chased the cat, the cats' food,              the dog's bone and the cat's milk, the boy had a catapult",   old = {"cat", "dog"},   new = {"TIGER", "WOLF"},   removves = {".", ",", ";", "'", "s"},   replace =     let       split = Splitter.SplitTextByWhitespace()(atext)     in       List.Transform(         split,         (x) =>           List.Accumulate(             {0, 1},             x,             (s, c) =>               let                 test = Text.Remove(s, removves) = old{c}               in                 test           )       ) in   replace

But if I then take the test ( see below) true / false , and use it in the following I get the result  wanted,  not sure why this works any  insights as to how this works?   does the " if logical' re-set for the second pass of accumulate ? this is all i can think of .

let   atext     = "the cat's bed, the dogs chased the cat, the cats' food,              the dog's bone and the cat's milk, the boy had a catapult",   old = {"cat", "dog"},   new = {"TIGER", "WOLF"},   removves = {".", ",", ";", "'", "s"},   replace =     let       split = Splitter.SplitTextByWhitespace()(atext)     in       List.Transform(         split,         (x) =>           List.Accumulate(             {0, 1},             x,             (s, c) =>               let                 test = Text.Remove(s, removves) = old{c}               in                 if test then Text.Replace(s, old{c}, new{c}) else s           )       ),   result = Text.Combine(replace, " ") in   result    

 

  • Ignore my previous reply, i was having a dumb moment.
    Step by Step this is what your top code is doing and its issues:

    1. Splitter.SplitTextByWhitespace()  splits the sentence into a list of words.
    2. List.Transform  processes each word  x  with  List.Accumulate({0,1}, x, ...).
    3. For each word, it accumulates over indices  {0,1} :
      • c=0:  Text.Remove(s, removves)  strips  . ,  , ,  ; ,  ' ,  s  from the word, then compares the result to  old{0}  ("cat"), producing a boolean (true/false). This boolean becomes the new accumulator value  s .
      • c=1: it then tries  Text.Remove(s, removves)  again — but  s  is now a boolean, not text.  Text.Remove  requires a text value, so this step throws a type-conversion error ("We cannot convert the value True/False to type Text").

    Other issues:

    • new = {"TIGER", "WOLF"} is defined but never used.  There's no actual replacement happening, despite the variable name replace implying substitution.
    • removves strips the letter "s" from anywhere in the word, not just trailing plural "s". This would mangle words like "was" → "wa".
    • Because old{1}  ("dog") is never checked in a way that survives (the accumulator overwrites itself with a boolean after the first pass), even if the type error didn't occur, the "dog" comparison logic is broken.

    Net result:

    This query will fail at evaluation time with a type error on the second accumulation step for every word, it does not successfully identify or replace "cat"/"dog" occurrences with "TIGER"/"WOLF".

    Comparing that to your second code, this version fixes the type bug and actually does the replacement. Key differences:

    1. Accumulator now stays text-typed throughout
      1. test = Text.Remove(s, removves) = old{c} still computes the boolean, but it's only used inside an if, not stored as the accumulator.
      2. if test then Text.Replace(s, old{c}, new{c}) else s the accumulator s always remains a string: either the replaced text or the unchanged original. This avoids the previous "boolean fed into Text.Remove" type error.
    2. new is now actually used
      • When the stripped word matches old{c},  Text.Replace  swaps the original word text (s , punctuation/apostrophes intact) for new{c} e.g., "cat's" → since  Text.Remove("cat's", removves)  =  "cat" matches old{0}, it does  Text.Replace("cat's", "cat", "TIGER") → "TIGERs'" ... actually let's check:  Text.Replace  replaces the substring "cat" inside "cat's", giving "TIGER's". Punctuation around it is preserved, not stripped.
    3. Both indices now meaningfully checked
      • c=0 checks against "cat"/"TIGER", c=1 checks against "dog"/"WOLF", each independently applied to whatever s  currently is, so a word can be tested/replaced for cat, then (unchanged, since it won't match "dog") passed through for the dog check.
    4. Added  Text.Combine(replace, " ")
      • Rejoins the transformed word list into a single string, so the result is readable text, whereas the original returned a list of words with no recombination.

    Problems I still see:

    • Stripping "s" anywhere (not just trailing) still risks false matches (e.g., "dogs" → "dog" correctly, but any word containing an internal "s" gets mangled before comparison, though the replacement itself uses the original s , so display text is safer than before).
    • Words like "cats'" → stripped to "cat" → matches →  Text.Replace("cats'", "cat", "TIGER") → "TIGERs'" (plural/apostrophe artifacts remain, since only the substring "cat" is replaced, not the whole cleaned token).
    • Whitespace based splitting means original punctuation attached to words is retained in s, so replacements produce oddly suffixed results like "TIGER's", "WOLFs", "TIGERs'".

4 Replies

  • Dicken's avatar
    Dicken
    Icon for Post Prodigy rankPost Prodigy

    well that's pretty thorough.   thanks for taking the time. 

  • Hi Dicken​ 

    You can use List.PositionOf / List.Transform with a lookup

    let

     atext = "the cat's bed, the dogs chased the cat, the cats' food, the dog's bone and the cat's milk, the boy had a catapult",

        old = {"cat", "dog"},

        new = {"TIGER", "WOLF"},

         removves = {".", ",", ";", "'", "s"},

        split = Splitter.SplitTextByWhitespace()(atext),

        replace = List.Transform(

            split,

            (x) =>

                let

                    clean = Text.Remove(x, removves),

                    pos = List.PositionOf(old, clean)

                in

                    if pos <> -1 then new{pos} else x),

        result = Text.Combine(replace, " ")

    in

        result

    • Dicken's avatar
      Dicken
      Icon for Post Prodigy rankPost Prodigy

      that was not the question, the question related to  why if you return 'test' it returns and error,
      i did lay this all out.

  • Ignore my previous reply, i was having a dumb moment.
    Step by Step this is what your top code is doing and its issues:

    1. Splitter.SplitTextByWhitespace()  splits the sentence into a list of words.
    2. List.Transform  processes each word  x  with  List.Accumulate({0,1}, x, ...).
    3. For each word, it accumulates over indices  {0,1} :
      • c=0:  Text.Remove(s, removves)  strips  . ,  , ,  ; ,  ' ,  s  from the word, then compares the result to  old{0}  ("cat"), producing a boolean (true/false). This boolean becomes the new accumulator value  s .
      • c=1: it then tries  Text.Remove(s, removves)  again — but  s  is now a boolean, not text.  Text.Remove  requires a text value, so this step throws a type-conversion error ("We cannot convert the value True/False to type Text").

    Other issues:

    • new = {"TIGER", "WOLF"} is defined but never used.  There's no actual replacement happening, despite the variable name replace implying substitution.
    • removves strips the letter "s" from anywhere in the word, not just trailing plural "s". This would mangle words like "was" → "wa".
    • Because old{1}  ("dog") is never checked in a way that survives (the accumulator overwrites itself with a boolean after the first pass), even if the type error didn't occur, the "dog" comparison logic is broken.

    Net result:

    This query will fail at evaluation time with a type error on the second accumulation step for every word, it does not successfully identify or replace "cat"/"dog" occurrences with "TIGER"/"WOLF".

    Comparing that to your second code, this version fixes the type bug and actually does the replacement. Key differences:

    1. Accumulator now stays text-typed throughout
      1. test = Text.Remove(s, removves) = old{c} still computes the boolean, but it's only used inside an if, not stored as the accumulator.
      2. if test then Text.Replace(s, old{c}, new{c}) else s the accumulator s always remains a string: either the replaced text or the unchanged original. This avoids the previous "boolean fed into Text.Remove" type error.
    2. new is now actually used
      • When the stripped word matches old{c},  Text.Replace  swaps the original word text (s , punctuation/apostrophes intact) for new{c} e.g., "cat's" → since  Text.Remove("cat's", removves)  =  "cat" matches old{0}, it does  Text.Replace("cat's", "cat", "TIGER") → "TIGERs'" ... actually let's check:  Text.Replace  replaces the substring "cat" inside "cat's", giving "TIGER's". Punctuation around it is preserved, not stripped.
    3. Both indices now meaningfully checked
      • c=0 checks against "cat"/"TIGER", c=1 checks against "dog"/"WOLF", each independently applied to whatever s  currently is, so a word can be tested/replaced for cat, then (unchanged, since it won't match "dog") passed through for the dog check.
    4. Added  Text.Combine(replace, " ")
      • Rejoins the transformed word list into a single string, so the result is readable text, whereas the original returned a list of words with no recombination.

    Problems I still see:

    • Stripping "s" anywhere (not just trailing) still risks false matches (e.g., "dogs" → "dog" correctly, but any word containing an internal "s" gets mangled before comparison, though the replacement itself uses the original s , so display text is safer than before).
    • Words like "cats'" → stripped to "cat" → matches →  Text.Replace("cats'", "cat", "TIGER") → "TIGERs'" (plural/apostrophe artifacts remain, since only the substring "cat" is replaced, not the whole cleaned token).
    • Whitespace based splitting means original punctuation attached to words is retained in s, so replacements produce oddly suffixed results like "TIGER's", "WOLFs", "TIGERs'".