Stacked Find

Here is one implementation of a "Stacked Find". Feedback and/or alternate views welcome.

# Stacked Find DRAFT
Set Variable [ $list ; Value: "A¶B" ] 
// Set Variable [ $list ; Value: "A¶B¶" ] 
// Set Variable [ $list ; Value: "¶A¶B" ] 
// Set Variable [ $list ; Value: "A¶¶¶B" ] 
// Set Variable [ $list ; Value: "¶¶¶" ] 
# 
# Validate that the list is NOT empty.
Set Variable [ $list.as_string ; Value: Substitute ( $list ; ¶ ; "" ) ] 
If [ IsEmpty ( $list.as_string ) ] 
	Show Custom Dialog [ "List is empty" ] 
	# 1 = OK
	Halt Script
End If
# 
# Stacked Find
Set Variable [ $count ; Value: ValueCount ( $list ) ] 
Set Variable [ $i ; Value: 1 ] 
Set Variable [ $enterFindMode ; Value: 1 ] 
Loop
	Exit Loop If [ $i > $count ] 
	Set Variable [ $value ; Value: GetValue ( $list ; $i ) ] 
	If [ Length ( $value ) ] 
		If [ $enterFindMode ] 
			Enter Find Mode [ Pause: Off ] 
			Set Variable [ $enterFindMode ; Value: 0 ] 
		Else
			New Record/Request
		End If
		Set Field [ ListTag::Tag ; "==" & $value ] 
	End If
	# 
	Set Variable [ $i ; Value: $i + 1 ] 
End Loop
# 
Set Error Capture [ On ]
Perform Find [] 
Set Error Capture [ Off ]
# 

I might be tempted to shrink it down to this since empty find request seem to be no-ops anyway:

# Stacked Find
Set Variable [ $count ; Value: ValueCount ( $list ) ] 
Set Variable [ $i ; Value: 0 ] 

Enter Find Mode [ Pause: Off ] 
Loop
	Exit Loop If [ Let ( $i = $i + 1 ; $i > $count ) ] 
	New Record/Request
	Set Field [ ListTag::Tag ; "==" & GetValue ( $list ; $i ) ] 
End Loop

Perform Find []

EDIT: I noticed one potential problem with my implementation. Since you're doing a == find and you want to ignore empty list values, you would need to check if $value is empty before populating the query field.

3 Likes

I agree with Josh. Putting a little extra into the Exit Loop If statement makes the entire loop simpler. I'd rearrange the steps to avoid having an unused request.

# Stacked Find
Set Variable [ $count ; Value: ValueCount ( $list ) ] 
Set Variable [ $i ; Value: 1 ] 

Enter Find Mode [ Pause: Off ] 
Loop
	Set Field [ ListTag::Tag ; "==" & GetValue ( $list ; $i ) ] 
	Exit Loop If [ Let ( $i = $i + 1 ; $i > $count ) ] 
	New Record/Request
End Loop

Perform Find []
5 Likes

The simpler it gets, the more I appreciate it. One thing I am on the fence about, however, is that there's no escaping of reserved/special find chars done by this scripting. That's certainly not wrong, and, in fact, allows for more versatility, as it allows the consuming code to make use of FMP's special find chars. That said, part of me thinks I'd personally find it the most useful if it did do escaping, and I knew that I could hand it an arbitrary newline-delimited list of values and it would match any of them.

3 Likes

no-ops? Interesting. Thanks.

Good point re escaping. This is a start...

@escapeTextForFind_v2 updated to v3

https://www.briandunning.com/filemaker-custom-functions/edit.php?fn_id=2664

This is what should show on the Web site:

// @escapeTextForFind_v3
// param: text
// Char 92 AKA \ is used to escape chars including itself
Substitute ( text ;
[ Char ( 92 ); Char ( 92 ) & Char ( 92 ) ] ;
[ "!" ; "\!" ] ;
[ "\"" ; Char ( 92 ) & "\"" ] ;
[ "#" ; "\#" ] ;
[ "*" ; "\*" ] ;
[ "<" ; "\<" ] ;
[ "=" ; "\=" ] ;
[ ">" ; "\>" ] ;
[ "?" ; "\?" ] ;
[ "@" ; "\@" ] ;
[ "~" ; "\~" ] ;
[ "¶" ; "\¶" ] ;
[ "…" ; "\…" ] ;
[ "≤" ; "\≤" ] ;
[ "≥" ; "\≥" ]
)

Seems to work

1/2 // testing script
image

2/2 // Modify Last Find
image

2 Likes

Using my function FOR.list (sorry, fmfunctions.com is down again, I'll have to copy the code here).
It gets that simple.

Enter Find Mode [ pause: Off]
Loop
   Exit loop if [ FOR.list ( "i" ; $list )
   Set field [ table::field ; "==" & $i.value ]
   New Record
End Loop
Perform Find []

Function: FOR.list ( _iterator ; _list )

FOR.list ( _iterator ; _list )
Fabrice Nordmann, @1morethingTweet
www.1-more-thing.com


v1. Dec 2017

Use in an ExitLoop script step to process each value of a ¶ delimited list.


A script like:

Loop
	Exit Loop [ FOR.list ( "item" ; list ( "apple" ; "banana" ; "cherry" ))]
	Show Custom Dialog [ $item.value ]
End Loop


Will display 3 dialog boxes ("apple", "banana" and "cherry") and stop.

*/

Let ( _iterations = ValueCount ( _list ) ;

Case ( _iterations and not IsEmpty ( _iterator );
	Evaluate ( "Let ( $" & _iterator & "= $" & _iterator & "+1 ; \"\" )") &
	Evaluate ( "Let ( $" & _iterator & ".value = " & Quote ( GetValue ( _list  ; Evaluate ( "$" & _iterator ))) & "; \"\")" ) &
	Case (
		Evaluate ( "$" & _iterator ) <= _iterations ; 0 ;
		Evaluate ( "Let ([ $" & _iterator & "= \"\" ; $" & _iterator & ".value = \"\"] ; 1 )" )
	 );
	True // no iterations -> exit
)
)
4 Likes

WHaaaa, how have I never thought to create a CF to handle for loop boilerplate!? Great idea

Even though I use a clipping library for cutting/pasting code from the FileMaker clipboard, I'm thinking the same thing.

I have a Filemaker Object Archive that converts FMP clipboard data into UTF-8 text and stores it for analysis or for re-use. It's based on Dan Shockley's FmClipTools. I just logged into GitHub to grab the links for Dan's Clip Tools repo, and GitHub pointed me to his latest effort, which is an AppleScript wrapper around @jwilling's FM Rainbow Log.

Even though I'm using iTerm and it allows me to store regex based colourising rules in my profile (so my logs get colourised magically), I'm thinking I need to install Rust.


Question: If I spend all of next weekend watching Rust tutorials, who should I blame?

3 Likes

I have a few :slight_smile:
FOR
FOR.list
WHILE (must rename this one)

1 Like

I'm checking out your Archive and Recover scripts - I was hoping to do something similar in the FmClipTools project, but want to be careful to avoid making it too complicated.
@Malcolm - feel free to jump into the discussion linked below: you might have some insight. I haven't gotten around to actually doing the things discussed there yet:

EDIT: In the past, I've had a FileMaker based interface for storing/retrieving snippets of code, including with merge fields. That had the advantage of being able to name the merge fields in an interface, instead of inside an AppleScript code file. And, it meant that the text swapped in for the merge fields could be a FileMaker calculation result, too. But, the downside was having an interface built IN FileMaker when you might be developing inside a modal FileMaker window like Manage Database. Too bad it is no longer trivial to have a runtime app that is still officially supported.

@DanShockley
I duplicated my installed FileMaker app into an Application folder in my user folder. I am able to run FileMaker two times in parallel. So I can have open the Manage Database window in one FM and another file in the other FM.

That works like a charm! I need that nearly daily.

Both FM apps get their own icon with different colors. So in the dock I can distinguish them.
In the "blue" FM I open my "need also" databases. In the "orange" one the file to work on.

no need to move it out of the root application folder - just duplicate it there as many times as you want.
image

1 Like

I just don't like "FileMaker Pro 1". "FileMaker Pro 2", etc. in the Applications folder. This is my peculiar "aesthetic" feeling. That's why I do it the way described. :blush:

Great stuff!