JQ: количество объектов в группе для подмножества ввода - PullRequest
0 голосов
/ 25 сентября 2018

Мне нужно посчитать количество объектов в каждой группе с помощью JQ, но только для N самых последних объектов.

Пример ввода, для N = 3:

{"modified":"Mon Sep 25 14:20:00 +0000 2018","object_id":1,"group_id":"C"}
{"modified":"Mon Sep 25 14:23:00 +0000 2018","object_id":2,"group_id":"A"}
{"modified":"Mon Sep 25 14:21:00 +0000 2018","object_id":3,"group_id":"B"}
{"modified":"Mon Sep 25 14:22:00 +0000 2018","object_id":4,"group_id":"A"}

Ожидаемый результат:

{"A",2}
{"B",1}

Мне не удается даже выбрать подмножество на основе даты, которое сохранит структуру объектов: это лучшее, что мне удалось достичь:

 [
   .modified |= strptime("%a %b %d %H:%M:%S %z %Y") |
   .modified |= mktime |
   .modified |= strftime("%Y-%m-%d %H:%M:%S")
 ]  |
 sort_by(.modified) |
 .[] |
 {modified, object_id, group_id}

По некоторым причинам результаты все еще не отсортированы.

Мне также не удается преобразовать такой список в массив, чтобы выбрать только N самых последних записей.

И после этого мне потребуетсяподсчитывать количество объектов в группе каким-либо образом.


В целом, похоже, мне нужно чрезвычайно интуитивное объяснение того, как массивы и списки объектов преобразуются друг в друга, и как изменить некоторые из их полей иПосле этого для извлечения требуются только поля.Учебники, которые я нашел, к сожалению, не помогли.

Ответы [ 2 ]

0 голосов
/ 25 сентября 2018

В принятом ответе используется параметр командной строки -s, который требует, чтобы все входные данные помещались в память.Для очень больших наборов данных это может быть невозможно.

С момента выпуска jq 1.5 (в 2015 году) доступна альтернатива.Поэтому здесь представлено решение с эффективным использованием памяти, использующее inputs.

Основные функциональные возможности заключены в следующем фильтре jq:

# Return an array of n items as if by 
# [stream] | sort_by(filter) | .[-n:]
def maxn(stream; filter; n):
  def maxn:
    sort_by(filter) | .[-n :];
  reduce stream as $x ([]; . + [$x] | maxn);

Решение рассматриваемой проблемы (с N == 3) теперь можно получить только в трех дополнительных строках:

maxn(inputs; .modified | strptime("%a %b %d %H:%M:%S +0000 %Y") | mktime; 3)
| group_by(.class_id)[]
| {(.[0].class_id): length}

Обратите внимание, что это предполагает использование параметра командной строки -n.Если он опущен, первая строка ввода будет проигнорирована.

Large N

Для больших наборов данных, если значение N также велико, вероятно, стоит потрудиться настроитьвыше, чтобы использовать поддержку jq для бинарного поиска (bsearch) вместо sort_by.Аналогичным образом может быть целесообразно кэшировать значения mktime.

0 голосов
/ 25 сентября 2018

Предполагая, что ваш входной файл:

cat file
{"modified":"Mon Sep 25 14:20:00 +0000 2018","object_id":1,"class_id":"C"}
{"modified":"Mon Sep 25 14:23:00 +0000 2018","object_id":2,"class_id":"A"}
{"modified":"Mon Sep 25 14:21:00 +0000 2018","object_id":3,"class_id":"B"}
{"modified":"Mon Sep 25 14:22:00 +0000 2018","object_id":4,"class_id":"A"}

Вы можете попробовать следующее:

<file jq -s '
   [ .[] | 
     (.modified |= (strptime("%a %b %d %H:%M:%S +0000 %Y") | mktime)) 
   ] | 
   sort_by(.modified) |              # sort using converted time
   .[-3:] |                          # take the last 3
   group_by(.class_id) |             # group ids together
   .[] |                             
   {(.[0].class_id): length}'        # create the object using the id name and table length
{
   "A": 2
}
{
  "B": 1
}

Обратите внимание, что в моей системе опция %z из strptime isn 'т работает.Поэтому я заменил его на +0000 (который в любом случае не используется при преобразовании времени).

...