Reputation: 118
Each time I think I finally understood jsonnet, it comes around to hit me in the face ... -.-
I have something like the following:
local applyModifications(kp) = {
[topLvlKey]: {
[subKey]: myfunction(kp[topLvlKey][subKey])
for subKey in std.objectFieldsAll(kp[topLvlKey])
},
for topLvlKey in std.objectFieldsAll(kp)
};
I want to iterate over everything inside the first 2 levels of an object an apply some function there ...
Bascially that works ... But depending on if I use std.objectFieldsAll
or std.objectFields
, hidden fields are visible afterwards or missing completely.
How would/could I do this without touching the hidden "property"? I understand my problem is, that I use a object-comprehension here and (to refer to an error message) that those "Object comprehensions cannot have hidden fields"... But as far as I understand jsonnet, something-comprehensions are the only way to create for-loops, right?
Testcode:
// vim: set ts=2 sw=2 expandtab :
local myfunction(o) = o {
spec+: {
foo: 'bar'
}
};
local applyModifications(kp) = {
[topLvlKey]: {
[subKey]: myfunction(kp[topLvlKey][subKey])
for subKey in std.objectFieldsAll(kp[topLvlKey])
},
for topLvlKey in std.objectFieldsAll(kp)
};
local stack = {
fooService: {
fooResource: {
kind: 'PrometheusRule',
spec: {
groups: [
{ name: 'fooGroup', rules: [{ alert: 'fooAlert', expr: 'fooExpr' }] },
{ name: 'barGroup', rules: [{ alert: 'fooAlert', expr: 'fooExpr' }] },
],
},
},
},
fooService2:: {
fooResource: {
kind: 'PrometheusRule',
spec: {
groups: [
{ name: 'fooGroup', rules: [{ alert: 'fooAlert', expr: 'fooExpr' }] },
{ name: 'barGroup', rules: [{ alert: 'fooAlert', expr: 'fooExpr' }] },
],
},
},
},
};
local stack2 = applyModifications(stack);
{
modified: stack2
}
Upvotes: 2
Views: 10999
Reputation: 118
@sbarzowski :
Took me some time to undestand the difference and adopt the solution to my example (2 loops), but here is what I got and it seems to work:
local applyModifications(kp) = kp + {
[topLvlKey]: kp[topLvlKey] + {
[subKey]: myfunction(kp[topLvlKey][subKey])
for subKey in std.objectFieldsAll(kp[topLvlKey])
},
for topLvlKey in std.objectFieldsAll(kp)
};
PS: I'm using go jsonnet v0.17.0
Upvotes: 0
Reputation: 3020
Following @sbarzowski comment (full lesson I'd rather say) above, and btw being sure that you're using go-jsonnet
fwiw (brew install go-jsonnet
on macos), you can trick the visibility "merging" by modifying you last line of code as:
[...]
local stack2 = applyModifications(stack);
{
modified: stack + stack2
}
Upvotes: 0
Reputation: 2991
You can achieve what you want with inheritance.
local applyModifications(obj, f) =
obj + {
[x] : f(obj[x]) for x in std.objectFieldsAll(obj)
}
;
applyModifications({
visible: "foo",
hidden:: "bar",
}, function(x) x + " modified")
This is single level for clarity, but it should be straightforward create a two level version (if you have any trouble, let me know).
The reason it works is that :
is "default visibility" which takes the visibility of the field it overrides. (You also have force-visible fields with ::
).
That said, you're in awkward territory and it can usually be avoided. Objects in Jsonnet replace both objects (struct / class instances) and maps (dicts) from other languages. Even though both concepts are unified, OOP features don't always play nicely with data-structure features.
Usually you want to think of each object as either:
self
and super
, have hidden fields etc. You handle each field manually as each can have completely different meaning and behavior.It's expected to have data objects contain OOP objects and vice versa. The awkwardness ensues when one object is in some middle ground.
But as far as I understand jsonnet, something-comprehensions are the only way to create for-loops, right?
Comprehensions are not special. Don't think of them as "for loops" from imperative languages. Array comprehensions are basically a syntax sugar for std.map and std.filter (with arbitrary nesting).
Upvotes: 2