glom icon indicating copy to clipboard operation
glom copied to clipboard

How to access more than one field without creating a dict?

Open kurka opened this issue 5 years ago • 1 comments

Say I have the following data structure:

target = [{'a': 1, 'b': 1}, {'a': 2, 'b': 2}]

I want to create a spec that access both fields 'a' and 'b' at the same level, but instead of storing their values on a dictionary, I want to store on a tuple, so to achieve:

glom(target, spec) == [(1, 1), (2, 2)]

For example:

  • Accessing as a dictionary (works, but this is not what I want):
spec = [{'a': 'a', 'b': 'b'}]
[{'a': 1, 'b': 1}, {'a': 2, 'b': 2}]
  • Accessing in tuples (does not work):
spec = [('a', 'b')]

This does not work, as 'b' is interpreted as the spec of the next level, and not as a 'parallel' access at the same level.

I was able to obtain the desired result with:

spec = [({'a': 'a', 'b': 'b'}, lambda t: tuple(t.values())]

But I wonder if there is a more appropriate way? Thanks in advance!

kurka avatar Nov 19 '20 15:11 kurka

It depends somewhat on whether you want to access all fields, or named subset.

For all fields, the lambda t: tuple(t.values()) method is the most appropriate (it could also be rewritten as Invoke(tuple).specs(T.values())).

If you want to access a specific, static set of fields however, then you could use the new Fill mode:

glom(target, [Fill((T['a'], T['b']))])
# or
glom(target, [Fill((Path('a'), Path('b')))])

Although truth be told, the way Fill actually works is rather different from what I understood it to be from the docs. My understanding was that this would work for what you're trying to do, but it doesn't:

# not the expected result
glom(target, [Fill(('a', 'b'))])
>>> [('a', 'b'), ('a', 'b')]

mathrick avatar Apr 25 '21 06:04 mathrick