使用 jq 一次删除多个键

Posted

技术标签:

【中文标题】使用 jq 一次删除多个键【英文标题】:Deleting multiple keys at once with jq 【发布时间】:2016-07-13 15:22:37 【问题描述】:

我需要从一些 JSON 中一次删除多个键(使用jq),我正在尝试了解是否有比每次调用 map 和 del 更好的方法。这是我的输入数据:

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"
  
]

对于每个项目,我想删除数字、语言和国家/地区键。我可以用这个命令做到这一点:

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

效果很好,我得到了想要的输出:

[
  
    "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"
  
]

但是,我想了解是否有 jq 指定要删除的多个标签的方式,所以我不必有多个 map(del()) 指令?

【问题讨论】:

【参考方案1】:

您可以提供一个要删除的路径:

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

另外,考虑到,您可能更愿意将您想要的键列入白名单,而不是将特定键列入黑名单:

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

【讨论】:

【参考方案2】:

没有必要同时使用mapdel

您可以将多个路径传递给del,以逗号分隔。

这是一个使用“点式”路径表示法的解决方案:

jq 'del( .[] .Country, .[] .number, .[] .Language )' test.json
不需要引号(您可能会觉得它更易读) 不对路径进行分组(要求您在每个路径中重新键入一次.[]

这是一个使用“数组样式”路径表示法的示例,它允许您将具有公共前缀的路径组合起来,如下所示:

jq 'del( .[] ["Country", "number", "Language"] )' test.json
在“最后一个共同祖先”下组合子路径(在本例中是***列表迭代器.[]

peak 的回答使用了mapdelpaths,不过您似乎也可以单独使用delpaths

jq '[.[] | delpaths( [["Country"], ["number"], ["Language"]] )]' test.json
需要引号和单例数组的数组 要求您将其放回列表中(带有开始和结束方括号)

总的来说,为了简洁起见,我会选择数组样式的表示法,但知道做同一件事的多种方法总是好的。

【讨论】:

【参考方案3】:

Louis 在其answer 中提到的“数组样式”和“点样式”表示法之间的更好折衷。

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

jqplay


此表单还可用于从嵌套对象中删除键列表(参见 russholio 的 answer):

del(.a | .d, .e)

暗示你也可以选择一个索引来删除键:

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

或多个:

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

您可以使用range() 函数删除范围(切片表示法不起作用):

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

一些旁注:

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

如果密钥包含特殊字符或以数字开头,则需要用双引号将其括起来,如下所示:."foo$",否则为.["foo$"]

【讨论】:

【参考方案4】:

除了@user3899165的回答,我发现要从“子对象”中删除一个键列表

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

【讨论】:

【参考方案5】:

delpaths 也是值得了解的,也许没有那么神秘:

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

由于delpaths 的参数只是 JSON,因此这种方法对于程序删除特别有用,例如如果键名可以作为 JSON 字符串使用。

【讨论】:

delpaths 将设置为null 在提供的路径表达式中找到的键的值,它不会按照此处的要求删除键。 @LouisMaddox - 我已经使用 jq 1.3、1.4、1.5 和 1.6 测试了上面给出的程序,在所有情况下,结果都是删除了密钥。如果您对最近的投票负责,请不要忘记将其删除。谢谢。 我仍然认为这是一个不受欢迎的答案:OP 表示“我正在尝试了解是否有比每次调用 map 和 del 更好的方法”。有:使用单个del 调用,这也将路径名周围的方括号数量减少到2,而不是8:jq 'del( .[] ["Country", "number", "Language"] )' test.json。您的答案有效,但我发现它是潜在的混淆来源和潜在的静默错误(因为 delpaths 不会删除密钥,而是在保留密钥时设置为 null)。这在过去一直困扰着我,所以我推荐使用更简单的del 来代替。 (a) 没有声称使用 delpaths 是最好的方法,只是说它是 Q 中要求的更好的方法。(b) 即使指向不存在的键的路径是包括在内,未添加密钥,因此我不理解您对“保留密钥”的担忧。 我昨天刚被这个咬了一口,因此被否决了,就这么简单:-) 没有冒犯的意思。除非您编辑答案,否则 *** 不会让我取消投票。我对“保留密钥”的担忧是指在不使用 map 的情况下使用 delpaths,在这种情况下,它将将该密钥的值设置为 null,但保留密钥本身。

以上是关于使用 jq 一次删除多个键的主要内容,如果未能解决你的问题,请参考以下文章

Tableau 设计提示11 - 仪表板布局提示

jq 插件写法

如何一次从CloudFormation中删除多个全局二级索引?

如何在python中持续按住特定键一段时间?

如何在 Hive 中使用 regexp_replace() 一次删除多个字符?

将 sqlite 用于数据库队列的最佳实践