Is there no way to iterate over an associative array?

3,174 views
Skip to first unread message

Bjorn Tipling

unread,
Oct 19, 2010, 7:20:07 PM10/19/10
to Closure Templates Discuss
Seems like for in only works with range and foreach creates a for (;;)
that assumes an array. Is there no way to iterate over a key value
object?

Michael Bolin

unread,
Oct 20, 2010, 10:39:45 AM10/20/10
to closure-temp...@googlegroups.com
Sadly, no, I do not believe that there is.

I'm not sure if this could easily be addressed with a plugin (http://code.google.com/closure/templates/docs/plugins.html) either since it seems like you really want a new command like:

{forin $key, $value in $map}
  <li>{$key}: {$value}
{ifempty}
  No pairings!
{/forin}

Tom Ball

unread,
Oct 20, 2010, 12:15:57 PM10/20/10
to closure-temp...@googlegroups.com
Perhaps a plugin function that returns the keys from a map as a list?  Then you could do something like:

{foreach $key in keys($map)}
...
{/foreach}

Tom

Michael Bolin

unread,
Oct 20, 2010, 12:34:34 PM10/20/10
to closure-temp...@googlegroups.com
That's a good idea and would be much easier to implement.

Mike Samuel

unread,
Oct 20, 2010, 1:28:51 PM10/20/10
to closure-temp...@googlegroups.com
2010/10/20 Tom Ball <tb...@google.com>:

> Perhaps a plugin function that returns the keys from a map as a list?  Then
> you could do something like:
> {foreach $key in keys($map)}
> ...
> {/foreach}
> Tom

This seems like a good idea.
If it becomes a base function, then it would be a good optimization
target resolving to a simple JS for..in loop when compiled to JS.

How useful would people find it if some kind of destructuring (
http://wiki.ecmascript.org/doku.php?id=harmony:destructuring ) were
allowed where $key appears in Tom's example?

So
{foreach [ $key, $value ] in properties( $map )}
...
{/foreach}

Bjorn Tipling

unread,
Oct 20, 2010, 2:55:07 PM10/20/10
to Closure Templates Discuss
I think Tom's suggestion would be a good idea. Seems like a common use
case. If by destructuring you mean assigning two values at once
(something like maybe for k, v in iteritems(map)), that's also a
pretty nice.
On Oct 20, 10:28 am, Mike Samuel <mikesam...@gmail.com> wrote:
> 2010/10/20 Tom Ball <tb...@google.com>:
>
> > Perhaps a plugin function that returns the keys from a map as a list?  Then
> > you could do something like:
> > {foreach $key in keys($map)}
> > ...
> > {/foreach}
> > Tom
>
> This seems like a good idea.
> If it becomes a base function, then it would be a good optimization
> target resolving to a simple JS for..in loop when compiled to JS.
>
> How useful would people find it if some kind of destructuring (http://wiki.ecmascript.org/doku.php?id=harmony:destructuring) were
> allowed where $key appears in Tom's example?
>
> So
> {foreach [ $key, $value ] in properties( $map )}
>   ...
> {/foreach}
>
>
>
>
>
>
>
> > On Wed, Oct 20, 2010 at 7:39 AM, Michael Bolin <bolinf...@gmail.com> wrote:
>
> >> Sadly, no, I do not believe that there is.
>
> >> I'm not sure if this could easily be addressed with a plugin
> >> (http://code.google.com/closure/templates/docs/plugins.html) either since it
> >> seems like you really want a new command like:
>
> >> {forin $key, $value in $map}
> >>   <li>{$key}: {$value}
> >> {ifempty}
> >>   No pairings!
> >> {/forin}
>
> >> On Tue, Oct 19, 2010 at 7:20 PM, Bjorn Tipling <bjorn.tipl...@gmail.com>

Andrey Moskvitin

unread,
Oct 22, 2010, 9:31:51 AM10/22/10
to closure-temp...@googlegroups.com
> {foreach $key in keys($map)}
> ...
> {/foreach}

I think a more flexible option

{foreach $pair in keyValues($map)}
  Key:   {$pair.name}   -
  Value: {$pair.value}
  <br />
{/foreach}

This will allow the use of templates for handling data with unknown structure

Andrey

Kai Huang

unread,
Oct 26, 2010, 5:20:33 PM10/26/10
to Closure Templates Discuss
If this feature were to exist, how would you guys expect the entries
to be ordered? Would we just iterate based on the underlying map
implementation (in either JS or Java) and return the entries in
whatever arbitrary order? Note that this would not guarantee same
behavior across multiple runs, nor the same behavior in JS and Java.
Or would we always sort the entries alphabetically by key?

I don't see one option as clearly better than the other. Of course we
can make it more complicated by allowing the user to choose which
option, or even allowing the user to supply a sorting function
somehow. But the usefulness of the feature may not be worth it for
the extra complexity.

Tom Ball

unread,
Oct 26, 2010, 5:35:13 PM10/26/10
to closure-temp...@googlegroups.com
I think if a developer needs sorted data, then they should use a list instead of a map.  That said, it wouldn't hurt if the keys were alphabetically sorted first.  If the character sets are the same, should such a sort give the same results with Java or JavaScript?

Tom

Mike Samuel

unread,
Oct 26, 2010, 6:44:10 PM10/26/10
to closure-temp...@googlegroups.com
2010/10/26 Tom Ball <tb...@google.com>:

> I think if a developer needs sorted data, then they should use a list
> instead of a map.  That said, it wouldn't hurt if the keys were
> alphabetically sorted first.  If the character sets are the same, should
> such a sort give the same results with Java or JavaScript?

There is no iteration order specified for JavaScript objects, but
EcmaScript harmony will probably specify insertion order within each
prototype chain layer which is the same as LinkedHashMaps in java.

Whould only own properties be returned in JS for compatibility with Java?

Dolapo Falola

unread,
Nov 5, 2010, 11:11:23 AM11/5/10
to Closure Templates Discuss
Has anyone tried the proposed solution that returns the keys from the
map as a list?
The soy parser does not seem to like using functions as the data ref
inside of the foreach command.
I have a working keys function and the compilation fails in various
ways:

> {foreach $name in keys($foos)}
Invalid data reference in 'foreach' command text "$name in
keys($foos)".

> {foreach $name in {keys($foos)}}
Left brace '{' not allowed within a Soy tag delimited by single braces
(consider using double braces to delimit the Soy tag)

> {{foreach $name in {keys($foos)}}}
Last character in a Soy tag must not be a brace character (consider
inserting a space after the brace character)

etc

On Oct 26, 6:44 pm, Mike Samuel <mikesam...@gmail.com> wrote:
> 2010/10/26 Tom Ball <tb...@google.com>:
>
> > I think if a developer needs sorted data, then they should use a list
> > instead of a map.  That said, it wouldn't hurt if the keys were
> > alphabetically sorted first.  If the character sets are the same, should
> > such a sort give the same results with Java or JavaScript?
>
> There is no iteration order specified for JavaScript objects, but
> EcmaScript harmony will probably specify insertion order within each
> prototype chain layer which is the same as LinkedHashMaps in java.
>
> Whould only own properties be returned in JS for compatibility with Java?
>
>
>
> > Tom
>

Kai Huang

unread,
Nov 5, 2010, 5:13:19 PM11/5/10
to Closure Templates Discuss
Yeah, right now the 'foreach' command only recognizes a data reference
in the data="..." attribute. But off the top of my head, I don't
think there's any reason that it can't be changed to accept an
arbitrary expression.

Charles Ma

unread,
Mar 28, 2011, 7:54:23 PM3/28/11
to closure-temp...@googlegroups.com, Kai Huang
Hey, has anyone come up with a decent solution to this problem? It's really frustrating having to transform my datatypes before using it with every soy template, especially when I'm converting code from a different templating engine. 

If not, where's the best place to start on writing a plugin? Examples I can follow? I've only been using the javascript version and don't know the java API at all. I'm happy to write my own foreach if necessary just to get something usable working. 

Kai Huang

unread,
Mar 28, 2011, 8:36:01 PM3/28/11
to Closure Templates Discuss
If your intent is to modify 'foreach' such that it accepts an
arbitrary expression to iterate over (such as the plugin function that
Dolapo was trying to use above), then your modification would not be a
plugin. You'd have to modify the compiler code itself. But it
wouldn't be a difficult change. (If you want to do it, see
ForeachNode, where you'll want to change the usage of
ExpressionParser.parseDataReference() to
ExpressionParser.parseExpression() instead.)

Sorry we can't just push out a release for you right now (since inside
Google, this modification you desire is already done). We just don't
have the bandwidth right now to prepare a release, which entails a lot
of other issues. We may be able to push out a new release sometime
next quarter (Apr-Jun). We'll also try to target being able to accept
external contributions by end of the following quarter (Jul-Sep).

Christopher Peisert

unread,
Aug 12, 2012, 5:42:16 PM8/12/12
to closure-temp...@googlegroups.com
Foreach iteration using the keys() function is now supported. I just tested the following example:

/**
 * Test foreach with keys().
 * @param map A simple map.
 */
{template .iterateMap}
  <ul>
  {foreach $key in keys($map)}
    <li>key: {$key}, value: {$map[$key]}</li>
  {/foreach}
  </ul>
{/template}



On Sunday, August 12, 2012 4:42:10 AM UTC-7, zonski wrote:
Is there an official solution for this or is it still a case of rolling our own?

I want to be able to build a form based on an arbitrary map as per the above.  

e.g. something like:  

    {foreach $key in keys($myMap)}
        {$key} = {value($myMap, $key)}
    {/foreach}

Seems like a pretty fundamental thing so I'm a little surprised it's not there by default and this thread is a tad old. 

The keys($map) function seems to exist and works under normal circumstances, but not within a foreach, and I can't see anything that I could use for the 'value' part. 

zonski

unread,
Aug 13, 2012, 7:06:29 AM8/13/12
to closure-temp...@googlegroups.com
Thanks Christopher, that worked perfectly!

My main problem was that I was using an older version from Tom's Maven repository, but I also needed the syntax for $map[$key]. 

Cheers for your help!

zonski
Reply all
Reply to author
Forward
0 new messages