Изменить массив JSON с входными данными из массива оболочки за один проход - PullRequest
0 голосов
/ 22 декабря 2018

Можно ли отфильтровать весь массив элементов в JQ всего за один проход?Сравните следующий код, который запускается jq снова и снова:

{
      "foofoo": {
        "barbar": [
          {
            "foo": "aaaa",
            "bar": 0000
          },
          {
            "foo": "bbbb",
            "bar": 1111
          },
          {
            "foo": "cccc",
            "bar": 2222
          }
          ]
      }
}

массив bash:

array=("1111" "2222")

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

for k in "${array[@]}"; do 
    jq  --argjson k "$k"  '.foofoo.barbar |= map(select(.bar != $k))' json.json | sponge json.json 
done

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

любые идеи, как добиться аналогичного поведения сболее легкий код?

Желаемый вывод:

{
  "foofoo": {
    "barbar": [
      {
        "foo": "aaaa",
        "bar": 0
      }
    ]
  }
}

Ответы [ 3 ]

0 голосов
/ 22 декабря 2018

Для значительного повышения производительности используйте следующий подход jq (без каких-либо оболочек петель):

arr=("1111" "2222")
jq '($p | split(" ") | map(tonumber)) as $exclude 
    | .foofoo.barbar 
      |= map(select(.bar as $b 
                    | any($exclude[]; . == $b) | not))' \
    --arg p "${arr[*]}" file.json | sponge file.json

Вывод:

{
  "foofoo": {
    "barbar": [
      {
        "foo": "aaaa",
        "bar": 0
      }
    ]
  }
}
0 голосов
/ 23 декабря 2018

Создание объекта словаря открывает дверь к эффективному решению.Если ваш jq имеет INDEX/2, вы можете использовать следующий вызов:

jq --arg p "${arr[*]}" '
  INDEX($p | split(" ")[]; .) as $dict
  | .foofoo.barbar 
      |= map(select($dict[.bar|tostring] | not))' 

Если ваш jq не имеет INDEX/2, то сейчас будет отличное время для обновления;в противном случае, вы можете перехватить его защиту, прибегнув к помощи:

0 голосов
/ 22 декабря 2018

Я уверен, что есть лучшие способы сделать это: я просто бросаю вещи в jq, пока что-то не прилипнет к стене ...

# 1. in the shell, construct a JSON object string from the array => {"bbbb":1,"cccc":1}
printf -v jsonobj '{%s}' "$(printf '"%q":1\n' "${array[@]}" | paste -sd,)"

# 2. use that to test for non-membership in the jq select function
jq --argjson o "$jsonobj" '.foofoo.barbar |= map(select((.bar|in($o)) == false))' json.json

выводит

{
  "foofoo": {
    "barbar": [
      {
        "foo": "0000",
        "bar": "aaaa"
      }
    ]
  }
}

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

...