Потоковая передача без усечения - PullRequest
1 голос
/ 20 апреля 2019

У меня есть данные JSON формы ниже. Я хочу преобразовать его, превратив ключ каждой записи в поле этой записи в потоковом режиме. Моя проблема: я не знаю, как это сделать, не обрезая ключ и не теряя его. Я вывел необходимую структуру потока, см. Внизу.

Вопрос: как преобразовать входные данные в поток без потери ключа?

Данные:

{
  "foo" : {
    "a" : 1,
    "b" : 2
  },
  "bar" : {
    "a" : 1,
    "b" : 2
  }
}

Не потоковое преобразование использует:

jq 'with_entries(.value += {key}) | .[]'

получают:

{
  "a": 1,
  "b": 2,
  "key": "foo"
}
{
  "a": 1,
  "b": 2,
  "key": "bar"
}

Теперь, если мой файл данных очень очень большой, я бы предпочел потоковую передачу:

jq -ncr --stream 'fromstream(1|truncate_stream(inputs))`

Проблема: при этом усекаются ключи "foo" и "bar". С другой стороны, не обрезать поток и просто вызывать fromstream(inputs) довольно бессмысленно: это делает целую часть --stream бездействующей, а jq считывает все в память.

Структура потока следующая с использованием . | tostream:

[
  [
    "foo",
    "a"
  ],
  1
]
[
  [
    "foo",
    "b"
  ],
  2
]
[
  [
    "foo",
    "b"
  ]
]
[
  [
    "bar",
    "a"
  ],
  1
]
[
  [
    "bar",
    "b"
  ],
  2
]
[
  [
    "bar",
    "b"
  ]
]
[
  [
    "bar"
  ]
]

в то время как с усечением, . as $dot | (1|truncate_stream($dot | tostream)), структура:

[
  [
    "a"
  ],
  1
]
[
  [
    "b"
  ],
  2
]
[
  [
    "b"
  ]
]
[
  [
    "a"
  ],
  1
]
[
  [
    "b"
  ],
  2
]
[
  [
    "b"
  ]
]

Похоже, что для того, чтобы построить поток так, как мне нужно, мне нужно будет сгенерировать следующую структуру (я вставил [["foo"]] после завершения первой записи):

[
  [
    "foo",
    "a"
  ],
  1
]
[
  [
    "foo",
    "b"
  ],
  2
]
[
  [
    "foo",
    "b"
  ]
]
[
  [
    "foo"
  ]
]
[
  [
    "bar",
    "a"
  ],
  1
]
[
  [
    "bar",
    "b"
  ],
  2
]
[
  [
    "bar",
    "b"
  ]
]
[
  [
    "bar"
  ]
]

Превращая это в строку jq может потреблять, я действительно получаю то, что мне нужно (см. Также фрагмент здесь: https://jqplay.org/s/iEkMfm_u92):

fromstream([ [ "foo", "a" ], 1 ],[ [ "foo", "b" ], 2 ],[ [ "foo", "b" ] ],[["foo"]],[ [ "bar", "a" ], 1 ],[ [ "bar", "b" ], 2 ],[ [ "bar", "b" ] ],[ [ "bar" ] ])

получают:

{
  "foo": {
    "a": 1,
    "b": 2
  }
}
{
  "bar": {
    "a": 1,
    "b": 2
  }
}

Окончательный результат (см. https://jqplay.org/s/-UgbEC4BN8) будет:

fromstream([ [ "foo", "a" ], 1 ],[ [ "foo", "b" ], 2 ],[ [ "foo", "b" ] ],[["foo"]],[ [ "bar", "a" ], 1 ],[ [ "bar", "b" ], 2 ],[ [ "bar", "b" ] ],[ [ "bar" ] ]) | with_entries(.value += {key}) | .[]

1049 * получая *

{
  "a": 1,
  "b": 2,
  "key": "foo"
}
{
  "a": 1,
  "b": 2,
  "key": "bar"
}

1 Ответ

2 голосов
/ 20 апреля 2019

Универсальная функция atomize(s) для преобразования объектов в объекты значения ключа представлена ​​в jq Cookbook . Используя это, решение проблемы здесь просто:

atomize(inputs) | to_entries[] | .value + {key}

({key} - сокращение от {key: .key}.)

Для справки вот def:

атомизации (s)

# Convert an object (presented in streaming form as the stream s) into
# a stream of single-key objects
# Example:
#   atomize(inputs) (used in conjunction with "jq -n --stream")
def atomize(s):
  fromstream(foreach s as $in ( {previous:null, emit: null};
      if ($in | length == 2) and ($in|.[0][0]) != .previous and .previous != null
      then {emit: [[.previous]], previous: ($in|.[0][0])}
      else { previous: ($in|.[0][0]), emit: null}
      end;
      (.emit // empty), $in
      ) ) ;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...