Сложный фильтр jq

Отказ от ответственности: это не домашнее задание, это очищенная версия того, что я делаю по работе.

Я пытаюсь отфильтровать некоторые данные json с помощью jq и хочу вернуть объект для каждой соответствующей записи в моем фильтре. Вот текстовое описание того, что я делаю: «Учитывая объект JSON, содержащий список студентов, некоторую личную информацию и их оценки, вернуть список имени, возраста и совокупного среднего балла для каждого студента, получившего пятерку в 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. Какие-либо предложения?


person AJ.    schedule 21.07.2014    source источник


Ответы (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"
  }
]
person Jeff Mercado    schedule 21.07.2014
comment
Пробую это на jqplay.org, получаю ошибку: синтаксическая ошибка, непредвиденный QQSTRING_START - person AJ.; 22.07.2014
comment
Проверьте входы. Я написал это на jqplay.org и смог получить результат. Жаль, что мы не можем ссылаться на конкретный результат там. - person Jeff Mercado; 22.07.2014
comment
Не уверен, что проблема заключается в однострочной версии фильтра, поскольку приведенный выше пример является многострочным. Вот что я скопировал: .students | карта ({имя, возраст, средний балл: .cum_gpa, CSC101: .semesters|карта (.grades)|add|.CSC101 }) | карта (выберите (. CSC101 == A)) - person AJ.; 22.07.2014
comment
О, вы всегда можете удалить символы новой строки и сделать их одной строкой. Я разделил его на несколько строк только для удобства чтения. Попробуйте мою исправленную версию (я убрал лишнюю карту): .students | map({ name, age, gpa: .cum_gpa, CSC101: .semesters | map(.grades) | add | .CSC101 } | select(.CSC101 == "A")) - person Jeff Mercado; 22.07.2014
comment
Кстати, я вижу проблему, которая у вас возникла, когда вы скопировали исходный фильтр. Вы пропустили цитату вокруг A в конце. Ошибка должна быть связана с несбалансированными котировками. - person Jeff Mercado; 22.07.2014