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%]
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:
Because the data then is [Stefan, 50%]
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:
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:
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 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
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
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?
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
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
.
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!
@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:
CurrentValue
stack, not on its value.Outer
formula.” They say, “ah duh, makes sense.”I do agree that a Depth()
formula would be nice to have.
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 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
.
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.
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(
...
))
Variables! Or at least somewhat of an analog! Best solution I’ve seen mentioned so far…
I can’t help but think the CurrentValues.Nth()
idea still seems a bit clunky and not user-friendly.
Yeah, not original to me, but would be enormously powerful. I think implementing both OuterValue
and aliasing would be the best solution.
I agree that Nth
is not what most new users will reach for. It’s as though we’re being intentionally pedantic in order to be “pure”. The question to ask is, “what will users try? What question will they ask in the forums?”
I think things like CurrentValue.Outer()
, OuterValue
, PreviousValue
and the like are what I would try. Even Value.Previous()
where Value
defaults to CurrentValue
.
Linguistically, they’ve kinda backed themselves into a weird corner, because it would seem that CurrentValue
should always refer to a variable related to the current value, so if it ever relates to a variable that’s not the current value, like when it’s the previous value, that would seems weird. But language evolves, I prefer warts that work (read: finally give me nested for-loops, I’m begging you).
I feel that it part of this can be solved with really good UX. If the formula editor makes it abundantly clear that there are multiple values available, the user can use the down arrow to move through and select them. In this case, with clear labeling and presentation, the nomenclature becomes less important.