Simulate a For Loop (Reference position inside FormulaMap)

#1

So I was struggling with a problem where I needed to evaluate multiple lists simultaneously. A classic use case for a loop.

Coda doesn’t have built-in formulas for classic loops such as for or while. The closest thing being FormulaMap. It takes a list as input and runs every item through an expression. Inside the expression, you use CurrentValue to reference the current item being evaluated. As a result, CurrentValue can be of any type (row, date, etc).

But there’s no obvious way to get the key, or the item position inside the list.

Unless…

You make CurrentValue to be exactly the same as its position! You can do that using Sequence to create an auxiliary list.

Sequence(1,finalValue) // =[1,2,3...finalValue]

Then run FormulaMap over this list.

Sequence(1, finalValue).FormulaMap(CurrentValue*2) // =[2,4,6...finalValue*2]

Applying it to my case:

//List1 = [1,2,3]
//List2 = [5,7,1]
Sequence(1, List1.Count()).FormulaMap(List1.Nth(CurrentValue)+List2.Nth(CurrentValue)) // =[6,9,4]

The more general form would be:

Sequence(initialValue, finalValue, step).FormulaMap(expression(CurrentValue))

More or less equivalent to

for (i = initialValue; i <= finalValue; i+=step) {
  expression // use i here
}

Since CurrentValue is a number from the sequence, it serves the purpose of the i.

3 Likes

SUMPRODUCT for more than 2 lists/arrays?
#2

Very nice writeup! I use this trick a lot and this a very clear writeup of the technique.

Btw as a sidenote, we have an ongoing debate about (1) the naming of FormulaMap and (2) if we were to add a more generic For or While loop syntax, how would it work? Got any ideas?

1 Like

#3

@shishir

@Lloyd_Montgomery and I recently discussed FormulaMap():

I run into these context issues often, and usually have to rely on trial and error to arrive at a solution, without ever truly understanding why that solution works and another doesn’t.

Would appreciate some official clarification on this!! :smiley:


Full discussion here: Automatic Scheduling of Todos

1 Like

#4

I’ll get to the syntax at the bottom, but conceptually I believe a for loop would have to be an ⚡Action, since it doesn’t return anything.

(After writing that I realize now I don’t think a loop would be a better solution for my initial problem in this post - the better one would be having the key keyword inside FormulaMap).


There are a few problems with iterative formulas in Coda regarding scope and evaluation of values. What @Ander pointed out is definitely one.

Another one is they all use the same alias for CurrentValue, so when you nest them you lose the parent’s context. I guess this could be solved by allowing we to call their parent, as in parent.CurrentValue and parent.parent....


Finally, a loop would also need to modify external data in real time. Correct me if I’m wrong, but the way I see it FormulaMap (or every other formula) makes a local copy of its arguments and evaluates everything then returns a result. So you can’t make a step rely on the result of the previous one.

For example, take this table

image

and add this to a button

Sequence(2,10).FormulaMap(AddRow(Table12, Column 1, Table12.Column 1.Nth(CurrentValue-1).Power(2)))

Initially you’d expect to run AddRow 9 times, squaring each previous row, but actually the sequence halts after the first step, and you get

image

That is even explicit when you edit the formula, it evaluates to a list of actions with only one valid action

image

Which becames obvious when you get it that every time Nth() is evaluated Table 12 contains only one row.


After all that am I wrong in extrapolating that every reference to a table or data inside a formula is not really a pointer to the real thing but just a local snapshot? (The exception being as the first parameter of an Action). This would be the root of lots of confusing moments.

Maybe a pointer syntax would be good then (although very resource demanding?) or maybe I’m overcomplicating things…

I really like the intuitive way we use functions without having to name variables and arguments, it’s one of the appealing features of Coda. I can see why you would avoid it. So the syntax problem is a real one for you devs haha.

1 Like

#5

Just posted a more straightforward way to make loops (which is also a while loop):

0 Likes

#6

Did you ever find a solution for referencing a parent CurrentValue in a nested FormulaMap?

0 Likes

#7

I’m struggling with this exact problem right now.

Sometimes I can do it by exploiting this bug, but it’s far from ideal.

0 Likes