phoeneous
phoeneous

Reputation: 59

Insert value into JSON array if it is not already present

I'm building a bash script that will read the response from an API and insert a value within a [] list (not {} array) if the value isn't present. Fake example response from API:

#response.json contains:

{
    "foods": {
        "menu": [
            "tacos",
            "spaghetti",
            "pizza",
            "chicken_florentine",
            "bacon_cheeseburge",
            "chow_mein",
            "sushi",
            "chocolate",
            "whiskey"
        ]
    }
}

The variable from my bash script is order="lasagna". If 'foods.menu[]' contains $order then do nothing, else insert $order value into 'foods.menu[]'.

Using bash variable order="lasagna" which currently doesn't exist in 'foods.menu[]', the resulting json should be:

{
    "foods": {
        "menu": [
            "tacos",
            "spaghetti",
            "pizza",
            "chicken_florentine",
            "bacon_cheeseburge",
            "chow_mein",
            "sushi",
            "chocolate",
            "whiskey",
            "lasagna" <----
        ]
    }
}

I started with trying a bash for loop and variations of jq's if-then-else, select, and contains but went down a rabbit hole. Any help is appreciated.

Upvotes: 3

Views: 115

Answers (3)

pmf
pmf

Reputation: 36078

Here's another way using the alternative operator // to default to the array's length for the item's index:

jq --arg order 'lasagna' '
  (.foods.menu | .[index($order) // length]) = $order
' response.json

Adding a list of items in the same manner:

jq '.foods.menu |= reduce $ARGS.positional[] as $order (.;
  .[index($order) // length] = $order
)' response.json --args 'lasagna' 'sushi' 'milk'

Upvotes: 1

oguz ismail
oguz ismail

Reputation: 50750

You don't need a loop for that

jq --arg order lasagna '.foods.menu |= if index($order) then . else . + [$order] end' response.json

But you may need one for inserting multiple orders

jq 'reduce $ARGS.positional[] as $order (.;
  .foods.menu |= if index($order) then . else . + [$order] end
)' response.json --args apple banana sushi

Upvotes: 6

glenn jackman
glenn jackman

Reputation: 246807

I don't know if this fits with any unspoken requirements.

Add the item regardless, and then unique the results:

echo "$result" | jq --arg order pizza '.foods.menu |= (. + [$order] | unique)'
{
  "foods": {
    "menu": [
      "bacon_cheeseburge",
      "chicken_florentine",
      "chocolate",
      "chow_mein",
      "pizza",
      "spaghetti",
      "sushi",
      "tacos",
      "whiskey"
    ]
  }
}

The resulting array is sorted. If there were any duplicates in the input, those are gone.

Upvotes: 3

Related Questions