antun
antun

Reputation: 2297

Deleting multiple keys at once with jq

I need to delete multiple keys at once from some JSON (using jq), and I'm trying to learn if there is a better way of doing this, than calling map and del every time. Here's my input data:

test.json

[
  {
    "label": "US : USA : English",
    "Country": "USA",
    "region": "US",
    "Language": "English",
    "locale": "en",
    "currency": "USD",
    "number": "USD"
  },
  {
    "label": "AU : Australia : English",
    "Country": "Australia",
    "region": "AU",
    "Language": "English",
    "locale": "en",
    "currency": "AUD",
    "number": "AUD"
  },
  {
    "label": "CA : Canada : English",
    "Country": "Canada",
    "region": "CA",
    "Language": "English",
    "locale": "en",
    "currency": "CAD",
    "number": "CAD"
  }
]

For each item, I want to remove the number, Language, and Country keys. I can do that with this command:

$ cat test.json | jq 'map(del(.Country)) | map(del(.number)) | map(del(.Language))'

That works fine, and I get the desired output:

[
  {
    "label": "US : USA : English",
    "region": "US",
    "locale": "en",
    "currency": "USD"
  },
  {
    "label": "AU : Australia : English",
    "region": "AU",
    "locale": "en",
    "currency": "AUD"
  },
  {
    "label": "CA : Canada : English",
    "region": "CA",
    "locale": "en",
    "currency": "CAD"
  }
]

However, I'm trying to understand if there is a jq way of specifying multiple labels to delete, so I don't have to have multiple map(del()) directives?

Upvotes: 71

Views: 69750

Answers (6)

russholio
russholio

Reputation: 133

In addition to @user3899165's answer, I found that to delete a list of keys from "sub-object"

example.json

{
    "a": {
        "b": "hello",
        "c": "world",
        "d": "here's",
        "e": "the"
    },
    "f": {
        "g": "song",
        "h": "that",
        "i": "I'm",
        "j": "singing"
    }
}

$ jq 'del(.a["d", "e"])' example.json

Upvotes: 7

tquid
tquid

Reputation: 321

This question is very high in the google results, so I'd like to note that some time in the intervening years, del has apparently been altered so that you can delete multiple keys with just:

del(.key1, .key2, ...)

So don't tear your hair out trying to figure out the syntax work-arounds, assuming your version of jq is reasonably current.

Upvotes: 22

YenForYang
YenForYang

Reputation: 3294

A better compromise between "array-style" and "dot-style" notation mentioned in by Louis in his answer.

del(.[] | .Country, .number, .Language)

jqplay


This form can also be used to delete a list of keys from a nested object (see russholio's answer):

del(.a | .d, .e)

Implying that you can also pick a single index to delete keys from:

del(.[1] | .Country, .number, .Language)

Or multiple:

del(.[2,3,4] | .Country,.number,.Language)

You can delete a range using the range() function (slice notation doesn't work):

del(.[range(2;5)] | .Country,.number,.Language)  # same as targetting indices 2,3,4

Some side notes:

map(del(.Country,.number,.Language))
# Is by definition equivalent to
[.[] | del(.Country,.number,.Language)]

If the key contains special characters or starts with a digit, you need to surround it with double quotes like this: ."foo$", or else .["foo$"].

Upvotes: 16

Louis Maddox
Louis Maddox

Reputation: 5576

There is no need to use both map and del.

You can pass multiple paths to del, separated by commas.

Here is a solution using "dot-style" path notation:

jq 'del( .[] .Country, .[] .number, .[] .Language )' test.json
  • doesn't require quotation marks (which you may feel makes it more readable)
  • doesn't group the paths (requires you to retype .[] once per path)

Here is an example using "array-style" path notation, which allows you to combine paths with a common prefix like so:

jq 'del( .[] ["Country", "number", "Language"] )' test.json
  • Combines subpaths under the "last-common ancestor" (which in this case is the top-level list iterator .[])

peak's answer uses map and delpaths, though it seems you can also use delpaths on its own:

jq '[.[] | delpaths( [["Country"], ["number"], ["Language"]] )]' test.json
  • Requires both quotation marks and array of singleton arrays
  • Requires you to put it back into a list (with the start and end square brackets)

Overall, here I'd go for the array-style notation for brevity, but it's always good to know multiple ways to do the same thing.

Upvotes: 34

peak
peak

Reputation: 116880

delpaths is also worth knowing about, and is perhaps a little less mysterious:

map( delpaths( [["Country"], ["number"], ["Language"]] ))

Since the argument to delpaths is simply JSON, this approach is particularly useful for programmatic deletions, e.g. if the key names are available as JSON strings.

Upvotes: 6

user3899165
user3899165

Reputation:

You can provide a stream of paths to delete:

$ cat test.json | jq 'map(del(.Country, .number, .Language))'

Also, consider that, instead of blacklisting specific keys, you might prefer to whitelist the ones you do want:

$ cat test.json | jq 'map({label, region, locale, currency})'

Upvotes: 92

Related Questions