pagid
pagid

Reputation: 13877

Value map with JQ

I've a large JSON file where I'd like to transform some values based on some kind of mapping.

The data I have looks like:

[
    {"id":1, "value":"yes"},
    {"id":2, "value":"no"},
    {"id":3, "value":"maybe"}
]

And I'd like to transform that into a list like this:

[
    {"id":1, "value":"10"},
    {"id":2, "value":"0"},
    {"id":3, "value":"5"}
]

With the fixed mapping:

yes => 10
no => 0
maybe => 5

My current solution is based on a simple if-elif-else combination like this:

cat data.json| jq '.data[] | .value = (if .value == "yes" then "10" elif .value == "maybe" then "5"  else "0" end)'

But this is really ugly and I'd love to have a more direct way to express the mapping.

Thanks for your help

Upvotes: 4

Views: 12672

Answers (4)

Dirk Hoffmann
Dirk Hoffmann

Reputation: 1573

using a jq custom function to translate a value of an array of objects:

jq 'def fnYN:
  if   . == true  or . == "true"  then "yes"
  elif . == false or . == "false" then "no"
  else "unknown"
end;
. | map(.value |= fnYN)
| [  .[] | { id, value }  ]
'

input:

[
    {"id":1, "value":true},
    {"id":2, "value":"false"},
    {"id":3, "value":"maybe"}
]

result:

[
  {"id": 1, "value": "yes"},
  {"id": 2, "value": "no"},
  {"id": 3, "value": "unknown"}
]

Upvotes: 0

jq170727
jq170727

Reputation: 14715

Here is a solution which uses an "inline" object since the mapping is small:

map(.value = {"yes":"10","no":"0","maybe":"5"}[.value])

which can be shorted with |= as in peak's solution to:

map(.value |= {"yes":"10","no":"0","maybe":"5"}[.])

Upvotes: 3

peak
peak

Reputation: 116957

If one wants to avoid having to specify the mapping on the command line, then the following two variants may be of interest.

The first variant can be used with jq 1.3, jq 1.4 and jq 1.5:

def mapping: {"yes":"10","no":"0","maybe":"5"};
map(.value |=  mapping[.])

The next variant uses the --argfile option (available since jq 1.4), and is of interest if the mapping object is available in a file:

jq --argfile mapping mapping.jq 'map(.value |= $mapping[.])' data.json

Finally, in jq 1.5, other alternatives based on import are also available (!).

Upvotes: 5

Jeff Mercado
Jeff Mercado

Reputation: 134571

Since you're translating string values, you should be able to use a json object to hold the mappings. Then mapping would be trivial.

$ jq --arg mapping '{"yes":"10","no":"0","maybe":"5"}'
    'map(.value |= ($mapping | fromjson)[.])' data.json

Upvotes: 1

Related Questions