Как я могу исключить массивы и объекты, не исключая нулевые значения в фильтре jq?

Я не понимаю, почему при фильтрации всех элементов, которые являются объектами и массивами, затрагиваются также нулевые значения.

И я пока не нашел обходного пути.

Вход:

$ cat test.json
{
  "object": {
    "key1": 1,
    "key2": null,
    "key3": 3
  },
  "array": [ "a", null, "c" ]
}

Фактическое поведение, неожиданно отсутствующий вывод для нулевых значений:

$ jq '[ paths(select(type | IN("object", "array") | not)) as $path | { "\($path)": getpath($path) } ] | add' test.json
{
  "[\"object\",\"key1\"]": 1,
  "[\"object\",\"key3\"]": 3,
  "[\"array\",0]": "a",
  "[\"array\",2]": "c"
}

Желаемый (и ожидаемый) результат:

{
  "[\"object\",\"key1\"]": 1,
  "[\"object\",\"key2\"]": null,
  "[\"object\",\"key3\"]": 3,
  "[\"array\",0]": "a",
  "[\"array\",1]": null,
  "[\"array\",2]": "c"
}

Обновлено:

Как мы видим здесь, пути с нулевыми значениями выводятся, если фильтр не применяется:

$  jq '[ paths as $path | { "\($path)": getpath($path) } ] | add' test.json
{
  "[\"object\"]": {
    "key1": 1,
    "key2": null,
    "key3": 3
  },
  "[\"object\",\"key1\"]": 1,
  "[\"object\",\"key2\"]": null,
  "[\"object\",\"key3\"]": 3,
  "[\"array\"]": [
    "a",
    null,
    "c"
  ],
  "[\"array\",0]": "a",
  "[\"array\",1]": null,
  "[\"array\",2]": "c"
}
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
1
0
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

paths/1 (см. его определение) уже применяется select/1 с использованием предоставленного вами фильтра узлов. Ваш подход не сработал, потому что ваш фильтр узлов также применяет еще один select/1 к результату, вызывая фильтрацию ложных значений (null и false). Удалите select/1 из фильтра узлов, и все заработает:

[ paths(type | IN("object", "array") | not) as $path
  | { "\($path)": getpath($path) }
] | add
{
  "[\"object\",\"key1\"]": 1,
  "[\"object\",\"key2\"]": null,
  "[\"object\",\"key3\"]": 3,
  "[\"array\",0]": "a",
  "[\"array\",1]": null,
  "[\"array\",2]": "c"
}

Демо


В качестве альтернативы, особенно если вы включили select/1 для имитации применения ярлыков , таких как iterables/0 (выбор только массивов и объектов) или scalars/0 (наоборот), которые сами по себе также все реализованы на основе select/1 (таким образом, непригодны для использования здесь с paths/1), вы можете переключиться на нерекурсивный path/1 фильтр (без «s») и вручную включить рекурсию, например используя рекурсивный спуск ..:

[ path(.. | select(type | IN("object", "array") | not)) as $path
  | { "\($path)": getpath($path) }
] | add

# or

[ path(.. | scalars) as $path
  | { "\($path)": getpath($path) }
] | add

Демо


В зависимости от вашего конкретного варианта использования вас также может заинтересовать функция jq по преобразованию входных данных в «потоковое представление», которое уже предоставляет вам структуру [path, value] с использованием фильтра tostream/0 или опции --stream. Просто отфильтруйте элементы возврата (без значения), выбрав их по индексу 1.

jq '[tostream | select(has(1)) | {("\(first)"): last}] | add'
# or
jq --stream -s 'map(select(has(1)) | {("\(first)"): last}) | add'
# or
jq --stream -n 'reduce (inputs | select(has(1))) as [$p,$v] ({}; .["\($p)"] = $v)'

Хотя я до сих пор не до конца понимаю, почему мой подход не работает, ваше решение кажется мне намного лучше, проще и понятнее! Также существуют решения, использующие потоковые представления. Большое спасибо!

yaccob 23.09.2023 23:42

Чтобы прояснить то, чего я не понимаю: почему отрицание type | in ("object", "array") не верно для типа «null»?

yaccob 23.09.2023 23:51

@yaccob paths/1определен уже с использованием select с вашим пользовательским фильтром. Ваш подход не сработал, потому что вы обернули вокруг него еще один select, в результате чего были отфильтрованы ложные значения (null и false). Уберите select, и всё заработает: [ paths(type | IN("object", "array") | not) as $path | …

pmf 24.09.2023 00:07

@yaccob Сам запутался: использование scalars (определено как select(type|. != "array" and . != "object")), конечно, имеет ту же проблему (фильтрация значений false), поэтому я заменил всю эту часть ответа содержимым последнего комментария.

pmf 24.09.2023 00:07

теперь я понял проблему, многому научился из ваших четких ответов, большое спасибо

yaccob 24.09.2023 10:09

Другие вопросы по теме