根据嵌套值检索 Json 中的值

Retrieve a value in Json based on a nested value

我想检索给定特定 id 的投票,数据(对 govtrack.us) is stored in json and I am writing in python and intend to use js 的赞美。

例如输入 "Y000062" 应该产生 "Aye"

{
  "bill": {
    "congress": 114,
    "type": "hr"
  },
  "category": "passage",
  "votes": {
    "Aye": [
      {
        "display_name": "Abraham",
        "id": "A000374",
      },
      {
        "display_name": "Yarmuth",
        "id": "Y000062",
      }
    ],
    "Nay": [
      {
        "display_name": "Clyburn",
    "id": "C000537",
      },
    ]}}

在终端中,解决方案是 cat /ccc/114/votes/2015/H384/data.json | egrep 'Nay|Not Voting|Present|Yea|Aye|Y000062' | grep -B 1 'Y000062' |头-1 但是将其中继到 python 子流程似乎是一个笨拙的解决方案。

注意:在Json中,{}是一个对象,[]是一个数组

>>> d = {
...   "bill": {
...     "congress": 114,
...     "type": "hr"
...   },
...   "category": "passage",
...   "votes": {
...     "Aye": [
...       {
...         "display_name": "Abraham",
...         "id": "A000374",
...       },
...       {
...         "display_name": "Yarmuth",
...         "id": "Y000062",
...       }
...     ],
...     "Nay": [
...       {
...         "display_name": "Clyburn",
...     "id": "C000537",
...       },
...     ]}}
>>> 
>>> # store lookup value in variable to more easily change later
... lookup_value = 'Y000062'
>>> 
>>> # you're only concerned with the data in d['votes']
... for key, value in d['votes'].items():
...     # for each 'display_name' in the each vote type ('Aye' or 'Nay')
...     for element in value:
...         if element['id'] == lookup_value:
...             print(key)
... 
Aye
>>> 

下面是实现此功能的示例函数:

def get_vote_cast_from_id(id):
    global my_json
    for name, nested_values in my_json['votes'].items():
        if any(nested_value['id'] == id for nested_value in nested_values):
            return name
    else:
        return None

get_vote_cast_from_id("A000374")
# returns: "Aye"

get_vote_cast_from_id("random_id")
# returns: None

其中 my_json 是存储您的 JSON 对象的全局变量。

这里是三个使用jq的解决方案。如果一个人被限制只能投票一次,那么结果应该是一样的;否则,它们可能会有所不同,例如,如果一个人被记录为同时投票赞成和反对。

第一个解决方案假设每个人最多投票一次。如果满足这个假设,那么它也是最有效的,因为它使用 any/2,它具有 "short-circuit" 语义:

$ jq  --arg id Y000062 '.votes
| if any( .Aye[]; select(.id == $id) ) then "Aye" 
elif any( .Nay[]; select(.id == $id) ) then "Nay"
else "none"
end' votes.json

下一个解决方案报告 "Aye" 每个出现在赞成名单上的个体,如果没有发现出现在赞成名单上的个体,则只继续对反对名单做同样的事情:

$ jq  --arg id Y000062 '.votes
| ((.Aye[] | select(.id == $id) | "Aye") // 
   (.Nay[] | select(.id == $id) | "Nay")) ' votes.json

如果对个人可以出现在两个列表中的次数没有限制,第三种解决方案可能是合适的。它首先构造一个 [VOTE, ID] 对的流,然后选择感兴趣的 ID:

$ jq --arg id Y000062 '.votes
| ((.Nay[] | ["Nay", .id]),
   (.Aye[] | ["Aye", .id])))
| select(.[1] == $id)
| .[0]' votes.json

对于给定的输入(稍作改动以使其有效 JSON),所有三个解决方案的输出为:

"Aye"

(您可以使用 hjson 等命令行工具将 JSON-with-extra-commas 转换为 JSON。)

这是一个使用 tostream

的 jq 解决方案
  .votes
| tostream
| select(length==2) as [$p,$v]
| select($v == $id)
| $p[0]

如果此过滤器在 filter.jq 中并且示例数据在 data.json 中,则

jq -M --arg id Y000062 -f filter.jq data.json

生产

"Aye"