Harry
Harry

Reputation: 1742

How to update JSON using JQ and get the updated value

I have some data in JSON which I want to edit using JQ.

I have tried to use self explanatory variables here. Please let me know if the data is not clear enough.

[
  {
    "name": "Person1",
    "mobiles": [{
      "number": "11111",
      "calls": 2
    },{
      "number": "22222",
      "calls": 3
    }],
    "total": 5
  },
  {
    "name": "Person2",
    "mobiles": [{
      "number": "33333",
      "calls": 1
    },{
      "number": "44444",
      "calls": 2
    },{
      "number": "55555",
      "calls": 1
    }],
    "total": 4
  }
]

What I want is, given a mobile number and no. of calls made for that mobile on a particular day, update the json (update both the individual counter and the total)

Eg. Let's say for number 444444 calls made are 5, then the final json should be

[
  {
    "name": "Person1",
    "mobiles": [{
      "number": "11111",
      "calls": 2
    },{
      "number": "22222",
      "calls": 3
    }],
    "total": 5
  },
  {
    "name": "Person2",
    "mobiles": [{
      "number": "33333",
      "calls": 1
    },{
      "number": "44444",
      "calls": 7 // previous value was 2, added 5 more
    },{
      "number": "55555",
      "calls": 1
    }],
    "total": 9 // previous total was 4, added 5 more
  }
]

What I have tried so far

I am able to update the total calls but not the individual calls for the given number

map(select(.mobiles[].number == "44444").total = .total + 5) gives

[
  {
    "name": "Person1",
    "mobiles": [
      {
        "number": "11111",
        "calls": 2
      },
      {
        "number": "22222",
        "calls": 3
      }
    ],
    "total": 5
  },
  {
    "name": "Person2",
    "mobiles": [
      {
        "number": "33333",
        "calls": 1
      },
      {
        "number": "44444",
        "calls": 2 // How do I update this
      },
      {
        "number": "55555",
        "calls": 1
      }
    ],
    "total": 9 // This is correct
  }
]

Upvotes: 2

Views: 418

Answers (1)

peak
peak

Reputation: 116670

To update the relevant "calls" along the lines you were exploring:

map( (.mobiles[] | select(.number == $number) | .calls) += $incr)

where the values of $number and $incr can (for example) be specified on the command line, e.g.

jq --arg number 44444 --argjson incr 5 -f update.jq input.json

To update the relevant "total", you could use any/2:

map( if any(.mobiles[]; .number == $number) 
     then .total += $incr
     else . 
     end)

You could chain these two together, either literally, or (presumably more efficiently) by combining the two filters into a single map. For readability and maintainability, you might also want to define helper functions (i.e., using def).

Upvotes: 2

Related Questions