Outer CurrentValue access

Ah, I see now, it’s based on my formula above. Do take note, if there’s only one member on a date, the bulleted list will become:

  • Stefan
  • 50%

Because the data then is [Stefan, 50%]

1 Like

Hi @shishir, is there any progress on this matter? I’m running into the same issue and would like to know if I can avoid the reported work-arounds by waiting for the solution from Coda’s side. I do vote for the colored distinction too as I don’t use API… Thanks!

Unfortunately no. I don’t think we’ve arrived at a new pattern we feel confident in yet. Would be great to keep suggesting alternatives (like the ones above) to help when we pick this back up.

Meanwhile one more approach to work with multiple CurrentValue contexts in a live formula: make a full product of all combinations of inner and outer CurrentValue and iterate over that:

1 Like

Also, while working on that previous post I discovered the reason why this CurrentValue issue is not so easy to address, from design point of view.

First, I think everyone in Coda wants to avoid going with CurrentValue1, CurrentValue2 etc. From a software engineer point of view this doesn’t look elegant at all. Not only the suffixes 1 2 etc bear no meaning other than telling variables apart, I can see how this would break many formulas relying on CurrentValue being the one from current context.

However, with the way how Coda currently works with variables, we cannot have multiple CurrentValue (with different color coding) either.

The first reason is logical: what if the outer loop and the inner loop use values from the same table, e.g. Tasks.FormulaMap(Tasks.Filter(...))? The colors and types of an inner and an outer CurrentValue would be the same then.

The second reason, however, is technical. Turns out, variables ($$[variable:...] items) are actually distinguished by their name, not an ID (and it makes sense):

Even if you supply some random arguments to the $$[] reference formula in place of actual grid ID and object ID, Coda will ignore those and replace them with the grid ID of the collection that CurrentValue is iterating over (I believe for coloring purposes only). And if you keep the IDs but rename the variable, it will resolve to blanks:

ezgif.com-optimize (81)

Doing the same thing with a row reference would do the opposite: still point to the specified object ID and row/column ID, and replace the name to reflect the actual name instead.

So this is why it’s not possible to have two CurrentValue variables — because unlike row/column references, variables are referenced not by any internal IDs but by names.

(Also you could’ve just told us so :wink: I know you want to communicate the reasons in a simple way for the majority of non-technical users to understand. But sharing the technical reasons would not only be very appreciated among us power users, but we’d be able to pitch in with better educated suggestions.)

Given this knowledge, I have another approach in mind. Keep CurrentValue behavior as is, but introduce a new variable CurrentValues that would be a list of all CurrentValue down the function stack. I.e. CurrentValues.First() would always be the outermost one, CurrentValues.Last() would be the innermost one (always the same as CurrentValue), CurrentValues.Nth() would give access to intermediary ones if needed etc. Technically it’d be as simple as push()ing and pop()ing a value in/out of the array whenever you’re entering/exiting the nested context (i.e. in the same pieces of logic where CurrentValue is changed). I imagine this would be the simplest addition (no extra syntax required), an elegant one, and pretty much solve the pain point. @shishir

7 Likes

I guess this is a great perspective.
I fully support this idea: it’s keeping compatibility with the existing implementation (i.e. CurrentValue is always CurrentValues.Last()) and allows to have a full control of nested contexts.
Good point @Paul_Danyliuk :+1:

1 Like

Quick re-up on this thread since we’ve been discussing ways to address this (no promises yet, just trying to figure out the right design). Would love updated thoughts to one of the earlier posts with a few options for how to handle this (Outer CurrentValue access). Any new syntax for differentiating loop variables that people would find natural?

2 Likes

Hi @shishir,
I believe that so far @Paul_Danyliuk’s (Outer CurrentValue access) proposal would meet many requirements on the user’s perspective and on the background implementation impacts.

Having a List of CurrentValues, being the first (CurrentValues.First() or CurrentValues.Nth(1)) the outermost value and the last (CurrentValues.Last() or CurrentValues.Nth(CurrentValues.Count()) or by default the… current CurrentValue) the innermost would be rather intuitive, consistent with the current implementation would definitely address many targets.

Happy to contribute more, if needed.
Cheers

2 Likes

You have a framework for handling this with Automation, Step 1 Result, Step 2 Result, etc - could you build off that?

Building off of @Federico_Stefanato.

What if CurrentValue returns what we all know and love as CurrentValue. But, there are special formulas Outer and Inner (or perhaps Previous/Next) which accesses the current value stack.

So, given two lists of numbers [1, 2, 3, 4] and letters ["a", "b", "c", "d"] , where you want the output

1 a
2 b
3 c
4 d

You would use the formula:

[1, 2, 3, 4].FormulaMap(
  ["a", "b", "c", "d"].FormulaMap(
    Concatenate(CurrentValue.Outer(), CurrentValue)
  )
)

For multiple nested lists, Outer and Inner should be “subscriptable” or indexable. E.g. with the lists [1, 2, 3, 4], ["a", "b", "c", "d"], and ["w", "x", "y", "z"] with the desired output:

1 a w
2 b x
3 c y
4 d z

The formula would be:

[1, 2, 3, 4].FormulaMap(
  ["a", "b", "c", "d"].FormulaMap(
    ["w", "x", "y", "z"].FormulaMap(
      Concatenate(CurrentValue.Outer(2), CurrentValue.Outer(), CurrentValue)
    )
  )
)

This means that the default value of Outer when left unspecified is 1, which I think is in keeping with the behavior of Nth (which I think is smart anyway since 0 indexing can sometimes confuse people).

Outer and Inner should also be reverse subscriptable. For example, the same formula above could be written using Inner like this:

[1, 2, 3, 4].FormulaMap(
  ["a", "b", "c", "d"].FormulaMap(
    ["w", "x", "y", "z"].FormulaMap(
      Concatenate(CurrentValue.Inner(-2), CurrentValue.Inner(-1), CurrentValue)
    )
  )
)

This also would imply that you can use CurrentValue.Outer(0) and CurrentValue.Inner(0) as synonyms for CurrentValue.

2 Likes

Hi @cnr,
thanks for the exhaustive example.
It is indeed what I reported (and again, credit should primarily go to @Paul_Danyliuk who first outlined the concept).

My suggestion is that instead of creating an ad-hoc syntax for the CurrentValues stack, just rely on how Arrays are currently implemented, so that it can be analogous to other (Coda) contexts (also in terms of indexing coherence).

Plus, the an additional property of CurrentValue, depth, should represent the nesting level in a relative way.

From your extremely handy examples:

[1, 2, 3, 4].FormulaMap(
  ["a", "b", "c", "d"].FormulaMap(
    ["w", "x", "y", "z"].FormulaMap(
      Concatenate(
          CurrentValues.First(),  // equivalent: CurrentValues.Nth(1); CurrentValues.Nth(CurrentValue.depth - 2);
          CurrentValues.Nth(2),   // equivalent: CurrentValues.Nth(CurrentValue.depth - 1); btw, here CurrentValues.Nth(CurrentValue.depth - 1).depth would be, well, 2 :)
          CurrentValue            // our beloved friend keeps his meaning - equivalent: CurrentValues.Last(); useless, but still... - CurrentValues.Nth(CurrentValue.depth); CurrentValues.Nth(CurrentValues.Count());
      )
    )
  )
)

I hope it makes sense.

Thank you!

1 Like

@Federico_Stefanato the only issue I see with that approach is what happens if CurrentValue is already returning a list?

By having a special formula it’s always:

  1. Clear that you’re operating on the CurrentValue stack, not on its value.
  2. Much cleaner and simpler to explain. A new user comes in and says, “How do I get the current value from the loop outside the current one?” You reply, “the Outer formula.” They say, “ah duh, makes sense.”

I do agree that a Depth() formula would be nice to have.

1 Like

Hi @Connor_McCormick,
I think there is a little detail not clear, here…

Actually, CurrentValue is - and will be - a value holding the contextual element.
Being a List() an single item or a List() of List()
This should not change as it is today.

CurrentValues (with the s) is a List() of CurrentValue, therefore there are no semantical interferences.

Depth() should be an object on its own - such as CurrentDepth.

So, for instance:

List(List(1,2,3), List(4,5)).FormulaMap( 
   Format("Depth: {1}; Size: {2} {3}",
      CurrentDepth, CurrentValue.Count(), 
      CurrentValue.FormulaMap(
         Format("Depth: {1}; Value: {2} (Outer depth: {3}; Size: {4})", 
            CurrentDepth, 
            CurrentValue, 
            CurrentDepth -1
            CurrentValues.Nth(CurrentDepth -1).Count()
         )
      )
   ).BulletedList()
).BulletedList()

Would print

- Depth: 1; Size: 3
  - Depth: 2; Value: 1 (Outer depth: 1; Size: 3)
  - Depth: 2; Value: 2 (Outer depth: 1; Size: 3)
  - Depth: 2; Value: 3 (Outer depth: 1; Size: 3)
- Depth: 1; Size: 2
  - Depth: 2; Value: 4 (Outer depth: 1; Size: 2)
  - Depth: 2; Value: 5 (Outer depth: 1; Size: 2)

(Forgive me is I missed some parenthesis :slight_smile: the key is to understand the concept).

I hope it helps.

Edit:
Updated with the CurrentDepth additional object

Ah got it, CurrentValues is the plural of CurrentValue and contains within it multiple CurrentValues in a list. Yeah, that would make sense. That reduces the ad hociness of the solution, at the cost of being a bit more verbose.

Which do you think is more readable:

CurrentValues.Nth(CurrentValue.Depth() - 2)

or

CurrentValue.Outer(2)

Maybe something that could clarify a bit more (?? not sure, tough) would be to call the list ContextualValues and the innermost element - ContextualValues.Last() - would be our CurrentValue.

:thinking:

Hi @cnr,

Very good point.

Adding responsibility and complexity to the current CurrentValue would have a significant impact on existing code, therefore I guess we should try to add something, not change it.

So this would then become

CurrentValues.Nth(CurrentDepth - 2)

Which would be a good compromise, I’d say

The reason I would prefer a “standard” implementation (i.e relying on what is already existing, therefore List()) is that there is a symmetric behaviour.
Conceptual readability is always welcome, but it forces to have contextual syntaxes and this has a cost in terms of overall complexity and learning curve.

This is a personal opinion, of course and I totally see your point.

2 Likes

As I think about it more, aliasing would also be super beneficial. You could do things like this:

Table.FormulaMap(
  t := [Tasks].Filter(Done=false), 
  Sequence(Length(t)).FormulaMap(
    ...
))
1 Like