在jq中,选择可以是可选的(即,如果找不到任何内容-不要过滤掉元素)

时间:2018-11-16 13:51:51

标签: json shell command-line jq

我正在使用JQ过滤事物列表,然后针对发现的每个事物-通过从子键中提取值将其重新格式化为单个字符串。

问题是缺少子键之一,因此省略了整行。

请考虑以下示例文档:

{
  "items": [
    {
      "id": "A",
      "active": true,
      "tags": [
        { "name": "foo", "value": 1 }
      ]
    },
    {
      "id": "B",
      "active": true,
      "tags": [
        { "name": "foo", "value":1 },
        {"name": "bar", "value":2}
      ]
    },
    {
      "id": "C",
      "active": false,
      "tags": [
        { "name": "foo", "value":1 },
        { "name": "baz", "value":3 }
      ]
    }
  ]
}

现在,我想选择所有活动项,并为每行创建一个描述项ID的行以及其foobar标签的值。

最初,我已经做了类似的事情:

jq -r '
   .items[] | 
   select ( .active == true ) | 
   ( .id + " -> [" + 
     ( .tags[] | select( .name == "foo" ) | .value | tostring ) +
     ", " +
     ( .tags[] | select( .name == "bar" ) | .value | tostring ) +
     "]"
   )
'

但是由于bar仅包含在项目B中,因此项目A的行被过滤掉了。

有没有一种方法可以使子选择成为可选内容,以便如果选择失败,我会得到一个空字符串或类似的内容?

1 个答案:

答案 0 :(得分:2)

您可以使用if ... then ... else ... end或也许//来修补查询,但最好也解决其他一些问题,例如如下:

 .items[]
 | select ( .active == true ) 
 | .id + " -> ["
   + (.tags | map(select(.name == "foo") | .value) | join(";"))
   + ", "
   + (.tags | map(select(.name == "bar") | .value) | join(";"))
   + "]"

对于jq版本1.5或更早版本,您需要添加对tostring的调用,或使用字符串插值,例如

   .items[]
   | select ( .active == true ) 
   | .id 
     + " -> [\(.tags | map(select(.name == "foo") | "\(.value)") | join(";") ), "
     +      "\(.tags | map(select(.name == "bar") | "\(.value)" ) | join(";"))"
     + "]"

使用first和字符串插值的变量

.items[]
| select ( .active == true )
| (first(.tags[] | select(.name == "foo") | .value) // "")  as $v
| (first(.tags[] | select(.name == "bar") | .value) // "")  as $w
| "\(.id) -> [\($v), \($w)]"

使用字符串插值可以避免使用tostring的潜在麻烦。