如何使用 jq 和 map 从复杂 JSON 中获取对象的键值对? (活动活动)

How to get key value pairs of the objects from complex JSON using jq and map? (Active Campaign)

我关注 JSON。我想根据角色获取键值对对象。在此示例中有 3 个角色(Presenter、Approver、Customer),但可以有更多,因为它是动态的。

JSON

{
   "Presenter Name": "Roney",
   "Presenter Email": "roney@domain.com",
   "Approver Name": "Tim",
   "Approver Email": "tim@domain.com",
   "Customer Name": "Alex",
   "Customer Email": "alex@domain.com",   
   "Invoice": "001",
   "Date": "2022-02-14"   
}

使用 jq、map 的预期输出

{
    "Presenter": {
      "email_address": "roney@domain.com",
      "name": "Roney",
      "role": "Presenter"
    },
    "Approver": {
      "email_address": "tim@domain.com",
      "name": "Tim",
      "role": "Approver"
    },
    "Customer": {
      "email_address": "alex@domain.com",
      "name": "Alex",
      "role": "Customer"
    }
}

我已经试过了,但不知道下一步该怎么做。请指教

to_entries |map( { (.key): { name: .value, email_address:.value, role: .key} } ) | add

split 是 space 字符处的键,同时丢弃其中没有的任何项目。然后它将三个字段相应地分配给它们的值,使用 reduce 组合分组。

to_entries
| map(.key |= split(" ") | select(.key[1]))
| reduce group_by(.key[0])[] as $g ({};
    .[$g[0].key[0]] = (
      INDEX($g[]; .key[1]) | {
        email_address: .Email.value,
        name: .Name.value,
        role: .Name.key[0]
      }
    )
  )
{
  "Approver": {
    "email_address": "tim@domain.com",
    "name": "Tim",
    "role": "Approver"
  },
  "Customer": {
    "email_address": "alex@domain.com",
    "name": "Alex",
    "role": "Customer"
  },
  "Presenter": {
    "email_address": "roney@domain.com",
    "name": "Roney",
    "role": "Presenter"
  }
}

Demo

{ "Name": "name",  "Email": "email_address" } as $key_map |
to_entries |
map (
   ( .key | split(" ") | select( length == 2 ) ) as [ $role, $raw_key ] |
   [ $role, "role",             $role  ],
   [ $role, $key_map[$raw_key], .value ]
) |
reduce .[] as [ $role, $key, $val ] ( {}; .[ $role ][ $key ] = $val )

Demo 在 jqplay


在上面,我们从统一数据开始。具体来说,我们首先生成以下内容:

[
   [ "Presenter", "role",          "Presenter"        ],
   [ "Presenter", "name",          "Roney"            ],
   [ "Presenter", "role",          "Presenter"        ],
   [ "Presenter", "email_address", "roney@domain.com" ],
   [ "Approver",  "role",          "Approver"         ],
   [ "Approver",  "name",          "Tim"              ],
   [ "Approver",  "role",          "Approver"         ],
   [ "Approver",  "email_address", "tim@domain.com"   ],
   [ "Customer",  "role",          "Customer"         ],
   [ "Customer",  "name",          "Alex"             ],
   [ "Customer",  "role",          "Customer"         ],
   [ "Customer",  "email_address", "alex@domain.com"  ]
]

有冗余信息,但没关系。

然后,最后的简单 reduce 构建所需的结构。


.key | split(" ") | select( length == 2 )

可以换成更安全的

.key | match("^(.*) (Name|Email)$") | .captures | map( .string )

这是另一种不使用 group_by 的较短方法。相反,这直接使用 reduce 遍历初始对象,如果键遵循 space-separated role-key 模式,则立即相应地设置所有字段。

reduce (to_entries[] | .key /= " ") as {key: [$role, $key], $value} ({};
  if $key then
    .[$role] += {({Email: "email_address", Name: "name"}[$key]): $value, $role}
  else . end
)
{
  "Presenter": {
    "name": "Roney",
    "role": "Presenter",
    "email_address": "roney@domain.com"
  },
  "Approver": {
    "name": "Tim",
    "role": "Approver",
    "email_address": "tim@domain.com"
  },
  "Customer": {
    "name": "Alex",
    "role": "Customer",
    "email_address": "alex@domain.com"
  }
}

Demo