【问题标题】:Complex jq Filter复杂 jq 过滤器
【发布时间】:2026-01-08 12:30:02
【问题描述】:

免责声明:这不是家庭作业,这是我为工作所做的经过净化的版本。

我正在尝试使用 jq 过滤一些 json 数据,并希望为过滤器中的每个匹配记录返回一个对象。这是我正在做的事情的文字描述:“给定一个 JSON 对象,其中包含学生列表、一些个人信息和他们的成绩,返回一个包含每个学生的姓名、年龄和累积 gpa 的列表CSC101。”

这是测试 JSON 对象(程序输入):

{
    "students": [
        {"name": "John", "age": "19", "gender": "m", "from": "Tampa, FL", "cum_gpa": "3.83", "semesters": [
            {"name": "201302", "gpa": "3.67", "grades": {"CSC101": "A", "MAT101": "A", "PSY101": "B"}},
            {"name": "201401", "gpa": "4.00", "grades": {"CSC201": "A", "MAT201": "A", "HIS101": "A"}}
        ]},
        {"name": "Mary", "age": "20", "gender": "f", "from": "Chicago, IL", "cum_gpa": "3.50", "semesters": [
            {"name": "201302", "gpa": "4.00", "grades": {"CSC101": "A", "MAT101": "A", "ECO101": "A"}},
            {"name": "201401", "gpa": "3.00", "grades": {"CSC201": "B", "MAT201": "B", "HUM101": "B"}}
        ]},
        {"name": "Dan", "age": "20", "gender": "m", "from": "Seattle, WA", "cum_gpa": "3.33", "semesters": [
            {"name": "201302", "gpa": "3.33", "grades": {"CHE101": "B", "MAT101": "A", "PSY101": "B"}},
            {"name": "201401", "gpa": "3.33", "grades": {"CHE201": "A", "MAT201": "A", "HUM101": "C"}}
        ]}
    ]
}

这是我当前的过滤器表达式和结果:

cat test.json |jq -c '.students[]|select(.semesters[].grades.CSC101 == "A")|{name: .name, age: .age, gpa: .cum_gpa, CSC101: .semesters[].grades.CSC101}' 
{"name":"John","age":"19","gpa":"3.83","CSC101":"A"}
{"name":"John","age":"19","gpa":"3.83","CSC101":null}
{"name":"Mary","age":"20","gpa":"3.50","CSC101":"A"}
{"name":"Mary","age":"20","gpa":"3.50","CSC101":null}

它有点工作,但它会产生额外的输出。我只希望它产生具有 CSC101 条目的学期,而是返回任何参加 CSC101 的学生的所有学期。有什么建议吗?

【问题讨论】:

    标签: json jq


    【解决方案1】:

    试试这个过滤器:

    .students | map(
        {
            name,
            age,
            gpa: .cum_gpa,
            CSC101: .semesters | map(.grades) | add | .CSC101
        }
        |
        select(.CSC101 == "A")
    )
    

    这个想法是合并每个学生所有学期的所有成绩并获取CSC101 成绩。然后过滤掉成绩为 A 的学生。

    这会导致:

    [
      {
        "name": "John",
        "age": "19",
        "gpa": "3.83",
        "CSC101": "A"
      },
      {
        "name": "Mary",
        "age": "20",
        "gpa": "3.50",
        "CSC101": "A"
      }
    ]
    

    【讨论】:

    • 在 jqplay.org 上尝试这个,得到“错误:语法错误,意外的 QQSTRING_START”
    • 检查输入。我在 jqplay.org 上写了这个并且能够得到结果。太糟糕了,我们无法链接到那里的特定结果。
    • 不确定过滤器的单行版本是否存在问题,因为您上面的示例是多行的。这是我复制的内容:.students |地图({姓名,年龄,gpa:.cum_gpa,CSC101:.semesters|map(.grades)|add|.CSC101 }) |地图(选择(.CSC101 == A"))
    • 哦,您总是可以删除换行符并使其成为单行。为了便于阅读,我只将其拆分为多行。试试我的修改版(我删除了额外的地图):.students | map({ name, age, gpa: .cum_gpa, CSC101: .semesters | map(.grades) | add | .CSC101 } | select(.CSC101 == "A"))
    • 顺便说一句,我看到您在复制原始过滤器时遇到的问题。您最后错过了 A 周围的报价。错误一定是引用了不平衡的引号。