List.ContainsAny not working in List.Accumulate

donkey shrek

New Member
Joined
Nov 15, 2022
Messages
26
Office Version
  1. 365
Platform
  1. Windows
If I use Text.Contains to have 1 filter only, it works fine (e.g. only those containing "@").
However, I want to combine the filters into 1 query so there won't be so many steps. Therefore, I'm using List.ContainsAny instead.

I think it's because PQ cannot pick up List.Accumulate's "current" if you put it inside a list?

Power Query:
= List.Accumulate(Source[Email],
{},
(state, current) => if List.ContainsAny({current}, {"@", ".", "gmail"}) then List.InsertRange(state, List.Count(state), {current}) else state
)

Current Output:
Empty list

Source List:
1675220787173.png
 
Last edited:

Excel Facts

Test for Multiple Conditions in IF?
Use AND(test, test, test, test) or OR(test, test, test, ...) as the logical_test argument of IF.
In your code current is a text value. You convert it into a one element list by the curly braces {current}. Then you ask whether that one item list contains any of these values: "@", ".", "gmail". It will always return false unless that item is exactly one of those three text values (and none are so it returns an empty list).

I think you're really wanting to ask whether the text contains any of those values. There is Text.Contains function in M but not a Text.ContainsAny so maybe something like the following ( note that if you want it to be case sensitive then omit the Comparer.OrdinalIgnoreCase parameter of the Text.Contains function):

Power Query:
let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    Text_ContainsAny = (txt as text, lst as list) as logical => List.Accumulate(lst, false, (s,c)=> if Text.Contains(txt, c, Comparer.OrdinalIgnoreCase) then true else s),
    lst = List.Select(Source[Email], each Text_ContainsAny(Text.From(_), {"@", ".com", "gmail"}))
in
    lst

Book1
ABCD
1EmailTable1
2xyz@gmail.comxyz@gmail.com
3xyz123Dot.com
4xyzabcGMAIL
5Dot.com
6GMAIL
7123456
8
Sheet1
 
Upvote 1
Solution
In your code current is a text value. You convert it into a one element list by the curly braces {current}. Then you ask whether that one item list contains any of these values: "@", ".", "gmail". It will always return false unless that item is exactly one of those three text values (and none are so it returns an empty list).

I think you're really wanting to ask whether the text contains any of those values. There is Text.Contains function in M but not a Text.ContainsAny so maybe something like the following ( note that if you want it to be case sensitive then omit the Comparer.OrdinalIgnoreCase parameter of the Text.Contains function):

Power Query:
let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    Text_ContainsAny = (txt as text, lst as list) as logical => List.Accumulate(lst, false, (s,c)=> if Text.Contains(txt, c, Comparer.OrdinalIgnoreCase) then true else s),
    lst = List.Select(Source[Email], each Text_ContainsAny(Text.From(_), {"@", ".com", "gmail"}))
in
    lst

Book1
ABCD
1EmailTable1
2xyz@gmail.comxyz@gmail.com
3xyz123Dot.com
4xyzabcGMAIL
5Dot.com
6GMAIL
7123456
8
Sheet1
Thank you! I never knew about custom functions in PQ haha.
Could you explain why the 2nd parameter of List.Accumulate is false rather than a list?
 
Upvote 0
The second parameter of List.Accumulate is the initial state. So I defaulted to false because no text contains has been found initially. State gets changed to true if text contains finds a match and otherwise state remains unchanged. So if any text contains is found the list accumulate will return true and if none are found it returns false. List.Accumulate has a somewhat misleading name because it doesn't necessarily return a list, it can return any data type - whatever the final state is.

Another looping function in M is List.Generate. It does always return a list.

List.Accumulate is roughly kin to vba For each x in collection ...do some stuff ... Next It always loops n times where n is the list count
List. Generate is roughly kin to Do while condition is true ... do some stuff and add the result as the next item of the list ... loop It loops an unspecified number of times until the condition is false. If not careful with List.Generate you can create an infinite loop.
 
Upvote 0
Thanks for the info! How would you modify this code to turn it into Text_ContainsAll instead of Any?
 
Upvote 0
Also, how do I output the values in the list that matched in Text_ContainsAny?
I was thinking something like this:
Power Query:
= let counter = 0 in Table.AddColumn(Source, "Domain Found", each if Text_ContainsAny([Email], RandomList) then [Email] & " " & List.PositionOf(RandomList, counter) and counter + 1 else "Not found")

not too sure if that code logic makes sense
 
Upvote 0
Thanks for the info! How would you modify this code to turn it into Text_ContainsAll instead of Any?
Power Query:
Text_ContainsAll = (txt as text, lst as list) as logical => List.Accumulate(lst, true, (s,c)=> if not Text.Contains(txt, c, Comparer.OrdinalIgnoreCase) then false else s)
 
Upvote 0
Power Query:
Text_ContainsAll = (txt as text, lst as list) as logical => List.Accumulate(lst, true, (s,c)=> if not Text.Contains(txt, c, Comparer.OrdinalIgnoreCase) then false else s)
How do I return the values from the list that have been matched?
 
Upvote 0

Forum statistics

Threads
1,223,884
Messages
6,175,171
Members
452,615
Latest member
bogeys2birdies

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top