Keyed/indexed comprehension syntax

40 views
Skip to first unread message

Marcelo Cantos

unread,
Sep 15, 2022, 7:48:30 PM9/15/22
to Jsonnet

I’d love to be able to write this:

{
    [k]: v * v
    for k: v in {a: 1, b: 2, c: 3, d: 4, e: 5}
}
# output: { "a": 1, "b": 4, "c": 9, "d": 16, "e": 25 }

You might also offer k:: v to enumerate hidden fields and k::: v for all fields.

As an added bonus:

{
    [v]: i
    for i: v in ['z', 'y', 'x']
}
# output: {"x": 3, "y": 2, "z": 0}

Thoughts? If this gets a vote of confidence, I'm happy to have a crack at implementing it.

Marcelo Cantos

unread,
Sep 15, 2022, 9:01:56 PM9/15/22
to Jsonnet
(Please ignore the embarrassing mistake in the second example output.)

jer...@grafana.com

unread,
Sep 16, 2022, 7:56:05 AM9/16/22
to Jsonnet
You can use `std.objectFields()` and `std.objectFieldsAll()` for this:

local d = {a: 1, b: 2, c: 3, d: 4, e: 5};
{
    local v = d[k],
    [k]: v * v
    for k in std.objectFields(d)
}

No objections to adding syntactic sugar but I don't see the need for it either.

Marcelo Cantos

unread,
Oct 21, 2022, 3:30:34 AM10/21/22
to Jsonnet

I feel that it’s not so much a question of need, as one of bang for buck. This would be a very simple addition to the language and would bring it in line with virtually every mainstream language. Support for iteration over dict/map/object entries is almost ubiquitous. Most languages offer a library solution, such as JavaScript’s Object.entries(d), or a syntactic one, such as Go’s for k, v := range d. Jsonnet is very much the odd one out here.

Also, while using std.objectFields is never especially clean, in some instances it is distinctly awkward. For instance, the following code requires a somewhat ugly construct in the middle (for entry in [...]) to capture and reuse the object in a variable:

local flattenTree(o) =
    if std.type(o) != 'object' then { '': o } else {
    [name + if subname == '' then '' else '.' + subname]: entry[subname]
    for name in std.objectFields(o)
    for entry in [flattenTree(o[name])]
    for subname in std.objectFields(entry)
  };

This cleans up quite nicely with language support:

local flattenTree(o) =
  if std.type(o) != 'object' then { '': o } else {
    [name + if subname == '' then '' else '.' + subname]: subentry
    for name: entry in o
    for subname: subentry in flattenTree(entry)
  };

The latter is not only simpler, but its intent can be discerned almost at a glance.

Reply all
Reply to author
Forward
0 new messages