Фильтровать объект json, если ключи существуют в другом объекте

#json #bash #command-line #jq

#json #bash #командная строка #jq

Вопрос:

У меня есть два файла

file1.json

 {
  "a": "x1",
  "b": "y1",
  "c": "z1"
}
  

file2.json

 {
  "a": "x2",
  "b": "y2"
}
  

Поскольку a amp; b уже существует в file2, я хочу вывести новый объект, содержащий только c. Значения на самом деле не имеют значения.

 {
  "c": "z1"
}
  

Я пытался

 jq -s '.[0] | to_entries | map(select(.key | in(.[1]) | not)) | from_entries' temp1.json temp2.json
  

Но я получаю следующую ошибку:

 jq: error (at temp2.json:4): Cannot index string with number
  

Забавная вещь, когда я пытаюсь:

 jq -s '.[0] | to_entries | map(select(.key | in({"a": "x2", "b": "y2"}) | not)) | from_entries' file1.json file2.json
  

Я получаю правильный вывод. Так что просто кажется, что jq обрабатывает.[1] как int? и не как объект json.

Комментарии:

1. .key | делает вашу . строку, например "a" . Что вы ожидаете "a"[1] иметь в виду?

Ответ №1:

Этот «хак» — это то, что сделало это для меня до сих пор. Мне любопытно, есть ли что-нибудь лучше.

 jq -s '. as $input | $input[0] | to_entries | map(select(.key | in($input[1]) | not)) | from_entries' temp1.json temp2.json
  

Ответ №2:

Вот простой подход, использующий reduce :

 jq -n --argfile one file1.json --argfile two file2.json '
 reduce ($two|keys_unsorted)[] as $k ($one; delpaths([[$k]]))'
  

И вот более эффективный фильтр, который все еще использует delpaths :

 $one | delpaths($two|keys_unsorted|map([.]))
  

Ответ №3:

Вот общая функция для выполнения вычитания объекта:

 # remove from $x all the keys that occur in $y
def minus($x; $y):
  ((($x   $y)|to_entries) - ($y|to_entries))
  | from_entries;
  

Это можно использовать для решения проблемы различными способами, например, с помощью вызова:

 jq -n -f program.jq file1.json file2.json
  

где program.jq содержит указанное выше определение, за которым следует:

 minus(input; input)