While FileMaker does not provide for true iterators within its calculation engine, it does provide the Loop [] script step within its scripting engine. The following convention applies to iterations based on walking across data that may be contained within a variable.

The following convention is similar to that of a For loop within other languages.

Script steps for a For loop
Set Variable [ $itemCount ; ValueCount ( $someVariableWithListContent ) ]
Loop
  Exit Loop If [ Let ( $i = $i + 1 ; If ( $i > $itemCount ; Let ( $i = Null ; True ) ) ) ]
  Set Variable [ $topValue; Value:GetValue ( $someVariableWithListContent ; $i ) ]
  # Do steps here
End Loop

The efficiency of this convention is such that the counter, the exit condition and the counter reset are maintained within the same calculation dialog box - all within the Exit Loop If step.

Using $i vs. $counter

Any value can be used for the counter within a data loop. This standard does not enforce the use of $i as your variable name for the counter, however, it is suggested. The use of $i as a convention is simply a matter of what is commonly used by many other programming languages. You can certainly use $n, $counter or any other variation desired. Simply make sure this same variable is not used within an embedded loop.

 

 

  • No labels

17 Comments

  1. The For-like Exit Loop If condition wrapping the steps of incrementing the iterator and testing the exit condition in one calculation strikes me as a best practice rather than a standard.

    1. I agree with that to an extent.

      Here's the reason I put it here. When it comes to standards within other languages, I'm thinking of PHP/Pear here, the language itself provides a variety of methods for accomplishing the same thing - think For vs. While.

      Typically, the standards within a framework, ala Pear, versus any language standards, are an extension beyond the language standards for the sake of coherency across different solutions within that framework.

      Reference: Pear control structure standards

      For that reason, and because FileMaker only has a limited number of ways you can perform a data loop, I figured it would be nice to be able to see this type of structure from solution to solution.

      It truly is the most compact and readable method of doing a data loop that I've seen - thanks to Corn at Proof.

      However, if concensus is that this is more of a Best Practices then I'm cool with moving it there.

    2. I would have to agree that combination of the steps may not always be the best. There are many who develop in FIleMaker that for whatever reason or another can't comprehend that step where those who use PHP or C++ flavors get it right away.

      The part that I absolutely agree with is the use of the exit step as the very first thing. This does help prevent many instances where the exit condition may equal zero and prevent an infinite spin creating new records. Of course it's not failsafe, but it does add another layer of protection.

      One last question: why the use of a function to compare against an exit condition that does not change during loop iterations? While a small gripe, it just seems as if those calls could be prevented by setting a $var before the Loop starts.

      1. You're right on the excessive function calls. I'll fix it and move it to Best Practices

  2. Anonymous

    One of the cases where this construction does not work well is if you need $i to be zero of a set number on it's first time round as you have to explicitly set $i before the loop

    john renfrew

  3. Anonymous

    I don't see it mentioned here, is there some standardized nomenclature for nested iterators?  I personally have used $counter and $subcounter for my solution; if it is better to use $i instead of $counter, what should one call one's nested iterators?

    1. Conventionally, nested iterators tend to be the next several letters in the alphabet, so, $i, $j, $k, etc.

      However, this is a convention borrowed from mathematical notation, which places a premium on being concise rather than being easy to read. $i is only a recommendation; so if $counter and $subcounter are easier to follow, by all means stick with those.

  4. Anonymous

    We tend to use $theCount and $theCounter. For many iterators, we then use $theContactCount and theContactCounter and so on. That makes it easy to read and easy to know which iterator to use...

    Hal Gumbert, CampSoftware.com

    1. While the "Computer Science" in me says to use $i, $j, etc. I agree with human readable in certain circumstances:

      1. If the script can be modified by an audience outside the original developer / dev. team.
      2. The script (or script template) is intended to show concepts where $i, $k, etc. may prove confusing due to the context of what it's trying to accomplish for a particular business case.

      I don't know if I agree with your naming for the count vs. counter - to me it's easier to read $customerCount and $thisCustomer as plain english for example. "Counter" is only "er" away from "Count"...

      If the script isn't going to be modified or it's contents used outside the scope of a seasoned developer most folks who have had at least one year of basic CS courses at a college level interpret $i, $j, etc. iterators quicker than english language counterparts.

      There's also cases where $i iterations aren't representative of what's going on when the iterator isn't following an incrementing pattern like 1, 2, 3. I have a few places where I iterate based on a list of positions that could have the numbers 4, 7 and 24 for example which are line positions in a list of record numbers...

      1. Anonymous

        I think that it might be better, in your "customer" example, to use $customerIndex and customerCount. The variable name $thisCustomer implies that it is the value, not merely an index number. In your example, the $topValue variable could be named $thisCustomerID (or $thisCustomerName, etc.).

        This makes me think that $loopIndex and $loopCount (or $loopMax?) would be better for the iterating index variable and the value count, respectively. Or, if you want to tie the names to what they are looping over: $customerIndex and $customerCount. 

         

        -- Daniel A. Shockley

  5. I ran some speed tests on the iterator methods mentioned. The value of $itemCount was 100,000.

    secondscode
    2.5
    ExitLoopIf [Let( $i = $i + 1 ; $i > $itemCount )]
    3.9
    Set Variable [$i; Value: $i + 1]
    Exit Loop If [$i > $itemCount]
    14.5
    Exit Loop If [LoopCounter ( "i" ) > $itemCount ]
    1. Thanks for running these tests. My own test replicated your results:

      iterationsLetSet VariableLoopCounter
      10003858191
      100003766231928
      1000003773582319452

      Runtimes are in milliseconds (got using the fabulous Get ( UTCmSecs ) function, which I hope everyone will put in a feature request for official support). Let is consistently about 1/3rd faster than Set Variable, and about 80% faster than the LoopCounter function. That said, none of these are likely to hold a candle to the computational cost of whatever we're doing in a practical loop. This is only a meaningful difference for situations where computer time is more expensive than developer time.

      For what it's worth, I normally use a TextExpander snippet ("fmfl" for "FileMaker For Loop") with fill-in fields for the iterator variable and the number of iterations that expands to something like this:

      /* For $i from 1 to 10000 */

      Let ( $i = $i + 1 ; $i > 10000 )

      1. I'm glad you mentioned Get(UTCmSecs) function - I hadn't heard of it before. I was using ScriptMaster plug-in to get sub-second time.

        I just did another test, this time I gathered a value list of primary key field ID's for 50,000 records. Depending on the iterator method used, it took between 6.5 and 14 seconds. This is a real-world scenario of performing a task in a loop and the speed of this task can be cut in half by using the fastest iterator method. I would suggest that the best practice be appended to not recommend LoopCounter custom function for large loops (50,000+?). I don't have any issues with using it for small loops though.

        I also use a text expander for this, and there is more info on that here: Keyword expansion tools

  6. Anonymous

    In longer scripts with several loops there is a chance of overlapping $i/ forgetting to switch to $j etc.

    Therefor I use the following variation (it kills $i when exiting the loop; so I can happily use $i over and over without bothering if it still contains any value):

    Exit Loop If [ not Let ( $i = $i + 1 ; Case ( $i <= $max ; 1 ; 0 & Let ( $i = "" ; ""))) ]

    Thanks for your insights,

    Rewolfer.

     

     

    1. Clever; I like it! I may be updating my TextExpander snippet for "for" loops with this. The one downside is that, as a longer expression, it's less readable, especially to developers not familiar with the technique. In my loop iterator snippet, I open with a comment:

      /* For $i from 1 to $totalIterations */

      With a more complicated calculation, such a leading comment will be even more helpful.

    2. I like this idea too!  I just don't like that it start's with "not".  I am going to use this calculation instead...

      Exit Loop If [ Let( $i = $i + 1 ; If ( $i > $itemCount ; Let ( $i = "" ; True ) ) ) ]
  7. Anonymous

    Jeremy: In our team of 6 we use PhraseExpress (Windows). It comes with a server and so any modification of snippets is immediately available to the others.

    Daniel: Your rewrite of the code will be the first entry I will do tomorrow!

    Rewolfer.