jq icon indicating copy to clipboard operation
jq copied to clipboard

Docs: unclear explanation of `map_values()`

Open jbrains opened this issue 3 years ago • 3 comments
trafficstars

I love jq. Thank you.

I read the section of the Manual on map() and map_values(), and although I eventually believe I understand it, I found it hard to grasp the meaning of the text and only understood once I saw the example. I would like to propose a change to improve the text, but I don't want to do that until I better understand what you folks had had in mind when you wrote the text.

If you'd rather I merely propose a change in a PR, please comment saying so and I'll do it.

This is the text that confused me:

Similarly, map_values(x) will run that filter for each element, but it will return an object when an object is passed.

I think this means the following. Please tell me if I have it right or wrong.

  • if x is not an object, then map_values() behaves exactly like map().
  • if x is an object, then map_values() transforms only the values exactly like map() would, but then returns an object with the same keys and the new values.

Of course, those explanations are also terrible, but it's the best I can draft at this moment while my brain wants to get back to my current task. :)

Questions:

  • How well do I understand was map_values() does?
  • Is this what you meant to say by "...but it will return an object when an object is passed"? (I think so, but I'd like to clarify.)
  • Instead of answering this second question, would you prefer that I just submit a PR after I've written that text in a much nicer way? ;)

jbrains avatar Aug 24 '22 14:08 jbrains

Hey, I think you have understood it correctly. If your curious you can check builtin.jq for how it's implemented def map_values(f): .[] |= f; (map is def map(f): [.[] | f];). And if your really curious you can check _modify which is what |= gets rewritten to.

I suggest you make a PR and I like how you listed the cases. But it might take some time until it gets merged, the people with merge rights works a bit sporadically.

wader avatar Aug 24 '22 15:08 wader

Hej! Tusentack. Det var inte i går!

Thanks. I will keep my expectations quite reasonable. :+1:

jbrains avatar Aug 24 '22 17:08 jbrains

Det samma! hoppas allt är bra :)

You can read in #2305 about the status of jq, i really hope something can be sorted out.

wader avatar Aug 24 '22 18:08 wader

map/1 accepts arrays and objects as inputs but always produces an array because it's defined like this: def map(f): [.[] | f];.

map_values/1 accepts arrays and objects as inputs and produces the same kind of value.

So if you want to map over the values of an object but keep it an object, then you should use map_values.

nicowilliams avatar Jul 09 '23 15:07 nicowilliams

I think of jq's map/1 as similar to flat map functions in other languages, you can use it to map a single value to zero, one or more values.

[ 10, 20, 30 ] |
map(. + (-1, 0, 1))
# [9,10,11,19,20,21,29,30,31]
[ 1, 1, 1, 2, 4, 5, 8, 1 ] |
map(if . % 2 == 0 then ., "hi" else . + 1 end)
# [2,2,2,2,"hi",4,"hi",6,8,"hi",2]

I almost never use map_values/1, use .[] |= when I want to map a value to another value.

A reason why I prefer using map/1 for arrays even if .[] |= or map_values() could work is that in jq 1.6, if you use something that uses labels, try, foreach, ? or similar things (even indirectly e.g. using first/1 or a function that uses try somewhere) as RHS for |= (even indirectly passing it to map_values/1), it breaks. Using map/1 you avoid those problems because its implementation doesn't use |=.

[ range(10) | tostring ] | .[] |= try tonumber catch []
# jq 1.6:   [[],[],[],[],[],[],[],[],[],[]]
# expected: [0,1,2,3,4,5,6,7,8,9]
[ range(10) | tostring ] | map(try tonumber catch [])
# jq 1.6:   [0,1,2,3,4,5,6,7,8,9]
# expected: [0,1,2,3,4,5,6,7,8,9]

Note that this bug has been fixed on master

emanuele6 avatar Jul 09 '23 16:07 emanuele6

I almost never use map_values/1, use .[] |= when I want to map a value to another value.

Me too. And I almost never use map -- map(f) is just a one-character savings over [.[]|f]! :)

nicowilliams avatar Jul 09 '23 16:07 nicowilliams

Fixed by #2680

emanuele6 avatar Jul 10 '23 16:07 emanuele6