Jsonnet: Remove a field from an object

Created on 26 May 2017  路  14Comments  路  Source: google/jsonnet

I haven't found examples nor a way to easily remove a field from an object.

I can eventually iterate over all key and skip the one I want to remove to build a new object with dict comprehension, but the object would lost hidden fields.

enhancement

Most helpful comment

I've just realized that the proposed implementation of objectPop doesn't actually work. You can't have hidden fields in object comprehensions nor multiple fors there. So the best you can do AFAICT is something like:

objectPop(obj, key): { 
    [k]: obj[k] for k in std.objectFieldsAll(obj) if k != key
} 

And that loses the visibility information.

All 14 comments

std.objectFieldsAll(o) will return the hidden ones too. However all your hidden fields would become visible because of the : in { [f]: v for f in std.objectFieldsAll(o) }

Can you get the effect of removing a field just by marking it as hidden? x :: super.x

Is there any chance that dict comprehension can include to use hidden too?

One thing I'd like to do is generalize the object comprehension and merge it with the object literal. This means things like this will be possible:

local fields = std.range(1, 100);
{
    ["field" + i]: true for i in fields if i % 2 == 0,
    ["field" + i]:: true for i in fields if i % 2 == 1,
    lots_of_fields: true if std.length(fields) > 50,
}

There's another request in #90 to extend comprehensions.

What you can do is then:

{
    [k]: obj[k] for k in std.objectFields(obj),
    [k]:: obj[k] for k in std.objectFieldsAll(obj) if !std.objectHas(obj, k),
}

It's quite easy from that to build it with all fields except one, just put if k != "foo" at the end.

This isn't strictly the same as removing a field from an object though, what it's doing is creating a statically bound copy. E.g. if we took the above code and put it in a function clone(), then:

local obj = {x: 1, y: self.x * 2};
{
    obj1: obj {x: 10},
    obj2: clone(obj) {x: 10},
}

would yield:

{
    "obj1": { "x": 10, "y": 20 },
    "obj2": { "x": 10, "y": 2 }
}

@sparkprime Yes, extending object comprehension and with hidden field would solve this issue.
as you suggested:

objectPop(obj, key): { 
    [k]: obj[k] for k in std.objectFields(obj) if k != key, 
    [k]:: obj[k] for k in std.objectFieldsAll(obj) if !std.objectHas(obj, k) && k != key,
 } 

@sparkprime Yes, extending object comprehension and with hidden field would solve this issue.
as you suggested:

objectPop(obj, key): { 
    [k]: obj[k] for k in std.objectFields(obj) if k != key, 
    [k]:: obj[k] for k in std.objectFieldsAll(obj) if !std.objectHas(obj, k) && k != key,
 } 

Unfortunately, the object is not late bound anymore in this case.

@mbarbero
Yes. { [k]: obj[k] for k in std.objectFieldsAll(obj) } is not the same as obj. It is merely an object which has all the same values in the same fields. But self is fixed as this point (and super is affected as well).

That said, objectPop solution is still useful for objects which just hold data and don't use self or super.

Indeed, it's still useful. My comment was a bit "raw", sorry about that. I just wanted to note that, if jsonnet ever includes a solution to remove fields from an object, it would be great if the solution would retain the late bounding on target object.

I've just realized that the proposed implementation of objectPop doesn't actually work. You can't have hidden fields in object comprehensions nor multiple fors there. So the best you can do AFAICT is something like:

objectPop(obj, key): { 
    [k]: obj[k] for k in std.objectFieldsAll(obj) if k != key
} 

And that loses the visibility information.

I just ran into this, finding that replicas: null is not the same as excluding the replicas field in the manifest in kubernetes for a deployment object.

If the # of replicas is managed by a Horizontal Pod Autoscaler, then you want to ensure that you don't change the # of replicas each time you deploy.

could you do something like:

objectPop(obj, key): {
    if std.objectHas(obj, k) then
       [k]: obj[k]
    else
       [k]:: obj[k]
    for k in std.objectFieldsAll(obj) if k != key
} 

?

Not really. The comprehensions only support a single field pattern. Also you can't wrap the fields in ifs like that. If you have a proposition how that could work, we can look into it. Right now you can do it with two comprehensions (one for : and the other for ::) and +.

Perhaps a more direct way of handling this, would be to just add a builtin to remove a field. We would need to specify what it means for self and super. I'm also open to proposals.

Generally, I would recommend removing fields only for "plain" objects anyway and you care only about what fields evaluate to and not automagically maintaining relationships between them. By plain objects I mean objects where each field is visible and concrete (i.e. doesn't depend on other fields through self/super). In such case objectPop is doing what you want.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

davidzchen picture davidzchen  路  6Comments

sbarzowski picture sbarzowski  路  7Comments

mbrukman picture mbrukman  路  5Comments

griff4692 picture griff4692  路  4Comments

lukasheinrich picture lukasheinrich  路  7Comments