JQ: Добавить вложенные числа? - PullRequest
2 голосов
/ 05 октября 2019

В jq, как я могу добавить числа, вложенные в потоковые объекты?

Пример:

{"game":
    {"player1": {"score": 2}}}
{"game":
    {"player1": {"score": 4}}}

Я могу добавить эти номера с помощью двух вызовов jq:

$ cat foo.json | jq '.game.player1.score' | jq --slurp 'add'
6

Как это можно сделать одним jq звонком?

Кроме того, как добавить очки двух разных игроков отдельно?

{"game":
    {"player1": {"score": 2},
     "player2": {"score": 20}}}
{"game": 
    {"player1": {"score": 4},
     "player2": {"score": 40}}}

$ cat foo.json | jq '???'
{"player1": 6, "player2": 60} 

Ответы [ 4 ]

1 голос
/ 05 октября 2019

Во-первых, вот вариант первого решения @ OguzIsmail для первой проблемы. Он служит для проверки полезности универсальной функции:

   def sigma(s): reduce s as $x (0; .+$x);

При этом и с использованием параметра командной строки -n решение данной проблемы просто:

    sigma(inputs.game.player1.score)

Вторая проблема

В том же духе универсальности:

def sigmas(stream; f):
  reduce stream as $s (null;
      [., ($s | f)] | transpose | map(add));

sigmas(inputs | .game; [.player1.score, .player2.score])
| {player1: .[0], player2: .[1]}

Обратите внимание, что sigmas, как определено здесь, может обрабатывать произвольно много слагаемых. Еще более универсальное решение, исключающее необходимость указывать слагаемые в виде списка, оставлено читателю в качестве (простого) упражнения: -)

jq.jq

Как правило, универсальные функцииможет быть включен в «стандартную библиотеку JQ». Например, если ваши служебные функции находятся в ~ / .jq / jq.jq, то, предполагая, что pwd не имеет другого jq.jq, вы можете написать (для решения первой проблемы):

jq -n 'include "jq"; sigma(inputs.game.player1.score)' foo.json

Надежное включение библиотеки

Чтобы избежать трудностей, связанных с путями модулей, иногда имеет смысл указывать путь в директиве include или import, например:

jq -n 'include "jq" {search: "~/jq"}; ...'
1 голос
/ 05 октября 2019

Один вариант - использовать reduce, например:

jq -n 'reduce inputs.game.player1.score as $score (0; . + $score)' file

Другой вариант:

jq -n '[inputs.game.player1.score] | add' file

Но это не будет работать так же хорошо, как первый с большими входами.


А вот более общий пример, касающийся второго Q тоже

jq -n 'reduce inputs.game as $game ({};
  reduce ($game|keys_unsorted)[] as $player (.;
    .[$player] += $game[$player].score
  )
)' file
0 голосов
/ 10 октября 2019

Вот решение с вспомогательной функцией, которая использует

  • Update assignment |= для выравнивания {"player1": {"score": 4}} объектов до {"player1": 4}
  • to_entries[] для преобразования строк в каноническую {"key":"player1", "value": 2} форму

Когда строки в канонической форме, простая Reduce выполняет окончательное агрегирование.

def kvrows: inputs[] | .[] |= .[] | to_entries[] ; # {"key":"player1","value": 2}...
reduce kvrows as $e ({}; .[$e.key] += $e.value)    # compute sums

Пример выполнения при условии, что в test.jq указано выше, а в test.json

$ jq -Mn -f test.jq test.json
{
  "player1": 6,
  "player2": 60
}
0 голосов
/ 05 октября 2019

Вот общее решение, которое возникает с точки зрения того, что поток объектов определяет таблицу (т. Е. Электронную таблицу). Решение также является эффективным (без излишеств) и надежным (без предположений о порядке или наличии ключей).

# The stream is assumed to consist of objects (or else arrays)
# in which same-named keys have compatible values under `+`.
# The resultant object (or array) has keys the values of which are
# the sum of the values of the corresponding keys of the
# input objects, which thus need not have the same keys.
def add_by_column(stream):
  def add(b): reduce (b|keys_unsorted[]) as $k
                (.; .[$k] += b[$k]);
  reduce stream as $x (null; add($x));


add_by_column(inputs | .game | map_values(.score) )

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...