Open MrGreenTea opened 6 years ago
Hey @MrGreenTea! I'm glad you've already discovered T. :)
Depending on what exact semantics you want, the latter solution is definitely most correct. @kurtbrose and I have been discussing some pattern matching possibilities which might clean this up, so it's a good thing you brought up an example.
For now I'd probably stick with the second version, unless @kurtbrose has some ideas. :)
This looks like a job for Check (as soon as I get it done...)
With Check, this would be the following:
glom(target, [Check('id', val=0)])
This is a perfect use case, I'll get on finishing up check and use this for the unit test.
@kurtbrose This looks exactly like what I just now wanted to suggest. That's awesome. Would this still require me choosing the first element afterwards?
oh, yea; I see id's are unique?
It would be nice if you could just express the dict that you wanted and then select out of it....
glom(target, ([T['id'], T], dict, T[0]) )
Currently there isn't a simple way to construct the list-of-iterables required for the dict.
(It can be done, but it is a bit wordy: glom(target, ([Call(tuple, args=[T['id'], T])], dict, T[0]))
)
Right now, lists are only valid in a glom if they have a single item.
Something else that has seemed to be a potential missing abstraction is some way to construct explicit tuples.
Oh no, even Call(tuple, args=[T['id'], T])
because that will do tuple(T['id'], T)
-- what should happen is tuple([T['id'], T])
-- although if you had a clean way to construct a list of two elements, you could skip the Call
because list-of-lists-of-two-elements is fine itself as an argument to the dict constructor...
Hmm.....
So, Check
still seems like the most direct way to solve this problem and was already on the roadmap / partly written (https://github.com/mahmoud/glom/blob/check/glom/core.py#L399-L447) so I'll proceed with that. Semantics for these kind of explicit lists might also be in the near future though.
Some thoughts --
Record([T['id'], T], type=tuple)
-- record is just very explicit about what is intended
[T['id'], T]
-- this reads nicely, but feels awkward -- list of 1 item means "unpack the target", list of 2 items means "join the results of multiple together"; seems error prone
Maybe another way to do this would be with zip
glom(target, (Call(zip, args=[[T['id']], T]), dict, T[0]) )
That last one works, but is quite awkward...
>>> target = [{'id': 0}, {'id': 1}]
>>> from glom import glom, T, Call
>>> from glom.core import Spec
>>> glom(target, (Call(zip, args=[Spec([T['id']]), T]), dict, T[0]) )
{'id': 0}
(You need to use the undocumented Spec()
so that Call
knows [T['id']]
is a sub-spec that should be evaluated and not a literal.)
I'll get back to work on Check
; honestly, if all you want to do is filter a single-level list like that, I think you're better off with a list comprehension
[e for e in target if e['id'] == 0][0]
even with Check
, it's probably better to use language features than a library unless there is a clear advantage to doing so
better dict literal syntax might help
glom(target, ({T['id']: T}, T[0]))
You're right about the list comprehension of course. It just felt natural to try with my nested data to extract single records. That's why I like the Record as a class.
I have some ideas for the Record class. I would love some feedback on it and if it fits your vision. I'd be glad to see if I'll manage to implement it then :) I am basing this on the current interface of Check. What do you think?
Record(path, instance_of=_MISSING, types=_MISSING, val=_MISSING, vals=_MISSING, exists=_MISSING, *, first=False)
# Checks for an element at path (like Check). If first==True, it returns the first one that's found. If first==False, it raises an error if an element cannot be uniquely.
The intention of Record
is to generate a tuple of items, akin to the list-of-tuples returned by fetchall()
in dbapi2 database APIs (e.g. sqlite3 in the standard library).
Some other possible names that would convey the same idea might be Struct
or Append
.
The basic API is Record(specifiers, type)
; an example usage would be Record(['a', 'b', 'c.d'], type=tuple)
which would give the result (target.a, target.b, target.c.d)
I understand. My idea was something similar to QuerySets.get() in Django.
in pattern matching there is the notion of a "guard expression" (came across it here https://www.reddit.com/r/Python/comments/725xam/adding_pattern_matching_to_python/dngkgde/)
extract(x) if guard:
...
Guard
is another alternative for Check
perhaps name-wise
Hey folks, just wanted to check, is glom.glom(target, ([lambda t: t if t['id'] == pk else glom.OMIT], glom.T[0]))
still the best solution, or is there a better way to do this now? Thanks
We've got a lot more parts to play with over the last two years:
([Or(M(T.id) == pk, default=SKIP)], T[0])
Hmm not sure if that is any clearer
I have some array of objects like this:
I now want to get the object with id=0 for example. Is there a way to do this with glom? My current solution looks like this and doesn't feel that elegant:
I also got it working with
which still looks not that clean to me.