Замена значения в структуре JSON на случайно выбранные строки из списка с помощью jq - PullRequest
0 голосов
/ 17 января 2019

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

{
  "features": [
    {
      "type": "Feature",
      "properties": {
        "name": "0",
        "height": 0.7
      }
    },
    {
      "type": "Feature",
      "properties": {
        "name": "1",
        "height": 0
      }
    }
  ]
}

Я хочу заменить только одно поле .features[].properties.name случайным значением из 1d-массива в другом текстовом файле. Я подготовил 8000 функций и около 100 имен.

Это то, что я сейчас получаю с ошибками:

#!/bin/bash
declare -a names=("name1" "name2" "name3")
jq '{
    "features" : [
        "type" : "Feature",
        "properties" : {
            "name" : `$names[seq 0 100]`,
            "height" : .features[].properties.height
        },
        .features[].geometry
    ]
}' < areas.json

Можно ли вообще выполнить одну команду или мне следует использовать python или js для таких задач?

Ответы [ 3 ]

0 голосов
/ 18 января 2019

Ваш документ (https://echarts.baidu.com/examples/data-gl/asset/data/buildings.json) на самом деле достаточно мал, чтобы нам не приходилось делать какие-то безумные уловки по сохранению памяти, чтобы заставить его работать; следующие функции как есть:

# create sample data
[[ -e words.txt ]] || printf '%s\n' 'First Word' 'Second Word' 'Third Word' >words.txt

# actually run the replacements
jq -n --slurpfile buildings buildings.json '
  # define a jq function that changes the current property name with the next input
  def replaceName: (.properties.name |= input);
  # now, for each document in buildings.json, replace each name it contains
  $buildings[] | (.features |= map(replaceName))
' < <(shuf -r words.txt | jq -R .)

Это работает, потому что shuf -r words.txt создает бесконечный поток слов, случайно выбранных из words.txt, а jq -R . внутри подстановки процесса указывает их как строки. (Поскольку мы вызываем input только один раз для элемента в buildings.json, мы не пытаемся продолжать работу после того, как содержимое этого файла будет полностью использовано).


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

{
  "features": [
    {
      "type": "Feature",
      "properties": {
        "name": "Third Word",
        "height": 0.7
      }
    },
    {
      "type": "Feature",
      "properties": {
        "name": "Second Word",
        "height": 0
      }
    }
  ]
}

... с фактическими словами, изменяющимися каждый пробег; он также был протестирован с полным внешним файлом.

0 голосов
/ 18 января 2019

Вот решение проблемы случайного выбора имен с заменой, используя очень простой PRNG, написанный на jq. скопировано с https://rosettacode.org/wiki/Random_numbers#jq

Призвание:

jq  --argjson names '["name1","name2","name3","name4"]' \
  -f areas.jq areas.json

areas.jq

# The random numbers are in [0 -- 32767] inclusive.
# Input: an array of length at least 2 interpreted as [count, state, ...]
# Output: [count+1, newstate, r] where r is the next pseudo-random number.
def next_rand_Microsoft:
  .[0] as $count | .[1] as $state
  | ( (214013 * $state) + 2531011) % 2147483648 # mod 2^31
  | [$count+1 , ., (. / 65536 | floor) ] ;

# generate a stream of random integers < $n
def randoms($n):
  def r: next_rand_Microsoft
    | (.[2] % $n), r;
  [0,11] | r ;


. as $in
| ($names|length) as $count
| (.features|length) as $n
| [limit($n; randoms($count))] as $randoms
| reduce range(0; $n) as $i (.;
    .features[$i].properties.name = $names[$randoms[$i]] )
0 голосов
/ 18 января 2019

Предполагая, что ваш Area.json является действительным JSON, тогда я считаю, что следующие действия приблизятся к выполнению вашего предполагаемого редактирования:

names='["name1","name2","name3","name4"]'
jq --argjson names "$names" '.features[].properties.name = $names
  ' < areas.json

Однако, учитывая ваше предложенное решение, мне не ясно, что вы подразумеваете под "случайным значением из 1d-массива". Если вы имеете в виду, что индекс должен выбираться случайным образом (например, с помощью PRNG), то я бы предложил вычислить его, используя ваш любимый PRNG, и передать это случайное значение в качестве еще одного аргумента для jq, как показано в следующем разделе.

Таким образом, возникает вопрос, как преобразовать текст

['name1','name2','name3','name4']

в допустимый массив JSON. Существует множество способов сделать это, используя jq или нет, но я считаю, что лучше оставить его как отдельный вопрос или как упражнение, потому что выбор метода, вероятно, будет зависеть от конкретных деталей, которые не упомянуты в этом вопросе. Лично я бы использовал sed, если это возможно; Вы можете также рассмотреть возможность использования , как также показано в следующем разделе.

Иллюстрация с использованием hjson и awk

hjson -j <<< "['name1','name2','name3','name4']" > names.json.tmp

function randint {
  awk -v n="$(jq length names.json.tmp)" '
    function randint(n) {return int(n * rand())}
    BEGIN {srand(); print randint(n)}'
}

jq --argfile names names.json.tmp --argjson n $(randint) '
  .features[].properties.name = $names[$n]
' < areas.json

Добавление

В настоящее время jq не имеет встроенного PRNG, но если вы хотите использовать jq и хотите, чтобы значение из массива «names» выбиралось случайным образом (с заменой?) Для каждого вхождения поля .name затем можно было бы предварительно рассчитать массив случайно выбранных имен (массив длиной features | length), используя ваш любимый PRNG, и передать этот массив в jq:

jq --argjson randomnames "$randomnames" ' 
  reduce range(0; .features[]|length) as $i (.;
    .features[$i].properties.name = $randomnames[$i]) 
  ' < areas.json

Другой вариант - использовать PRNG, написанный на jq, как показано в других местах на этой странице.

...