jq:按属性分组和键

Posted

技术标签:

【中文标题】jq:按属性分组和键【英文标题】:jq: group and key by property 【发布时间】:2017-08-30 11:50:18 【问题描述】:

我有一个如下所示的对象列表:

[
  
    "ip": "1.1.1.1",
    "component": "name1"
  ,
  
    "ip": "1.1.1.2",
    "component": "name1"
  ,
  
    "ip": "1.1.1.3",
    "component": "name2"
  ,
  
    "ip": "1.1.1.4",
    "component": "name2"
  
]

现在我想按组件对其进行分组和键入,并为每个组件分配一个 ips 列表:


  "name1": [
    "1.1.1.1",
    "1.1.1.2"
  ]
,
  "name2": [
    "1.1.1.3",
    "1.1.1.4"
  ]

【问题讨论】:

【参考方案1】:

我自己想通了。我首先按.component 分组,然后创建新的ips 列表,这些ips 由每个组的第一个对象的组件索引:

jq ' group_by(.component)[] | (.[0].component): [.[] | .ip]'

【讨论】:

如果密钥是数字类型怎么办? @branquito 它不能。对象键始终是 JSON 中的字符串。如果你有一个像 "0": 1 这样的 JSON,你可以使用 ."0" 获取 "0" 密钥。 此查询产生的输出不是有效的 json。您可以在我最近发布的这个答案中找到进一步的解释:***.com/a/69346071/2508466【参考方案2】:

作为@replay's technique的进一步示例,在使用其他方法多次失败后,我终于构建了一个过滤器来浓缩这个Wazuh报告(为简洁而摘录):


  "took" : 228,
  "timed_out" : false,
  "hits" : 
    "total" : 
      "value" : 2806,
      "relation" : "eq"
    ,
    "hits" : [
      
        "_source" : 
          "agent" : 
            "name" : "100360xx"
          ,
          "data" : 
            "vulnerability" : 
              "severity" : "High",
              "package" : 
                "condition" : "less than 78.0",
                "name" : "Mozilla Firefox 68.11.0 ESR (x64 en-US)"
              
            
          
        
      ,
      
        "_source" : 
          "agent" : 
            "name" : "100360xx"
          ,
          "data" : 
            "vulnerability" : 
              "severity" : "High",
              "package" : 
                "condition" : "less than 78.0",
                "name" : "Mozilla Firefox 68.11.0 ESR (x64 en-US)"
              
            
          
        
      ,
      ...

这是我用来提供对象数组的jq 过滤器,每个对象都包含一个代理名称,后跟一个代理易受攻击包的名称数组

jq ' .hits.hits |= unique_by(._source.agent.name, ._source.data.vulnerability.package.name) | .hits.hits | group_by(._source.agent.name)[] |  (.[0]._source.agent.name): [.[]._source.data.vulnerability.package | .name ]'

这是过滤器产生的输出的摘录:


  "100360xx": [
    "Mozilla Firefox 68.11.0 ESR (x64 en-US)",
    "VLC media player",
    "Windows 10"
  ]


  "WIN-KD5C4xxx": [
    "Windows Server 2019"
  ]


  "fridxxx": [
    "java-1.8.0-openjdk",
    "kernel",
    "kernel-headers",
    "kernel-tools",
    "kernel-tools-libs",
    "python-perf"
  ]


  "mcd-xxx-xxx": [
    "dbus",
    "fribidi",
    "gnupg2",
    "graphite2",
    ...

【讨论】:

【参考方案3】:

接受的答案不会产生有效的 json,但是:


  "name1": [
    "1.1.1.1",
    "1.1.1.2"
  ]


  "name2": [
    "1.1.1.3",
    "1.1.1.4"
  ]

name1name2 是有效的 json 对象,但作为一个整体的输出不是。

以下jq 语句会产生问题中指定的所需输出:

group_by(.component) | map( key: (.[0].component), value: [.[] | .ip] ) | from_entries

输出:


  "name1": [
    "1.1.1.1",
    "1.1.1.2"
  ],
  "name2": [
    "1.1.1.3",
    "1.1.1.4"
  ]

欢迎提出更简单方法的建议。

如果人类可读性优于有效的 json,我会建议类似 ...

jq -r 'group_by(.component)[] | "IPs for " + .[0].component + ": " + (map(.ip) | tostring)'

...导致...

IPs for name1: ["1.1.1.1","1.1.1.2"]
IPs for name2: ["1.1.1.3","1.1.1.4"]

【讨论】:

以上是关于jq:按属性分组和键的主要内容,如果未能解决你的问题,请参考以下文章

JSjs数组分组,javascript实现数组的按属性分组

核心数据:按实体属性对实体进行分组

XSLT 1.0:按属性分组

Swift NSFetchRequest 按一个属性分组

按属性对列表进行分组

SwiftUI:按核心数据属性分组和求和