tobgu/pyrsistent

Transformation that not strict with missing fields

horvatha opened this issue · 2 comments

I would suggest a version of the transform method, where there is no change if a field is missing.

My motivation is, that I like the idea of pyrsistent's tranformation, but I use document databases with optional fields and subfields and I would like to keep the original state where a field or parent field is missing. (Also pydantic can use Optional fields.) Coming up with some weird lambda function instead of ny would mess up the code.

I give you some examples:

data = v(
        m(timeout="13", description="send email"),
        m(description="start database"),
    )

loose_transform(data, (ny, "timeout"), int)
# or
data.transform((ny, "timeout"), int, mode="loose")

would give back

data = v(
        m(timeout=13, description="send email"),    # The only place that changes is timeout here.
        m(description="start database"),
    )

doesn't do anything with the second element.

A second example:

data = m(
    composer=m(birth=m(place="Salzburg", year=1777), name="Felix"),
    lyrics=m(name="John"),
    arranged_by=m(name="Jack", birth=m(year=1977))
)
result = loose_transform(data, [ny, "birth", "place"], str.upper)

gives back

m(
    composer=m(birth=m(place="SALZBURG", year=1777), name="Felix"),   # The only place that changes is place here.
    lyrics=m(name="John"),
    arranged_by=m(name="Jack", birth=m(year=1977))
)

doesn't create birth/place in lyrics and arranged_by. And I don't need to use a more difficult function instead of str.upper to handle the missing fields.

I have a loose_transform implementation and unit tests as gist.

Maybe pyrsistent could use the same transform method with an additional mode keyword argument, that is "strict" by default with the current behavior, and maybe "loose" is the behavior I presented here. (I'm not a native English speaker, so I couldn't find better word than loose.)

I think that discard could work with the same way, so using discard instead of str.upper in the second example would just remove place="Salzburg" from the composer field.

tobgu commented

Hi!

Thanks for opening this issue. The use case seems reasonable to me. Could you please create a PR with a suggested implementation for me to review (the gist looks fine in principle but I need to have a look at it in a pyrsistent-context) and eventually merge if you want it included in pyrsistent?

Using a new keyword arg to transform seems like the most discoverable way of doing I think.

My main concern is: the transform function's current version works for every pyrsistant data structure. As I would like to use JSON-like structures, I don't need any other than PVector and PMap. Is is ok if for the other data structures it just raises an exception if I don't use the current strict mode?
I'm not sure I can create any PR soon.