jq: подсчитывать значения вложенных объектов, которые удовлетворяют условию

json-данные:

{testId: '1' studentId:{'s1':{score: 20} 's2':{score: 80}}}
{testId: '2' studentId:{'s1':{score: 60} 's2':{score: 70}}}
{testId: '3' studentId:{'s5':{score: 40} 's7':{score: 30}}}
...

Я хотел бы использовать JQ, чтобы сказать мне, сколько студентов набрали > x баллов за каждый тест.

Таким образом, для приведенного выше ввода и x = 50 вывод:

{testId: '1' numStudents:1}
{testId: '2' numStudents:2}
{testId: '3' numStudents:0}

Я могу создать список всех студентов, набравших> 50 баллов по каждому тесту с

{testId, studentId: .studentId[]?} | select(.studentId.score>50)

Эта команда создает список объектов, каждый из которых содержит testId, studentId и оценку, где все оценки превышают 50. Однако я не знаю, как рекомбинировать эти результаты в нужный мне результат.


person lufthansa747    schedule 19.10.2016    source источник


Ответы (1)


Далее предполагается, что ввод состоит из потока действительных объектов JSON.

Один из подходов заключается в использовании add:

$ jq '{testId, numStudents: ([select(.studentId[].score > 50) | 1] | add // 0)}' 

При заданном вводе (после внесения исправлений) вывод будет:

{ "testId": "1", "numStudents": 1 } { "testId": "2", "numStudents": 2 } { "testId": "3", "numStudents": 0 }

Альтернативой может быть использование length, например. с этим фильтром:

{testId,
 numStudents: ([select(.studentId[].score > 50) ] | length)}

Однако, если вы не возражаете против добавления 'def', то лучшим решением (например, потому что оно не включает построение промежуточного массива) будет следующее:

def count(s): reduce s as $i (0; .+1);

{testId, numStudents: count(select(.studentId[].score > 50))} 
person peak    schedule 19.10.2016