使用jq从列表中随机选择的字符串替换JSON结构中的值

时间:2019-01-17 18:07:50

标签: json bash jq

有很多类似的问题,但没有一个问题可以动态加入2个文件。 我想做的是动态编辑以下结构:

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

我只想用另一个txt文件中1d数组中的随机值替换一个字段.features[].properties.name。我已经准备了8,000个功能和大约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 :(得分:2)

您的文档(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
      }
    }
  ]
}

...实际词每次运行都会有所不同;完整的外部托管文件也经过了烟雾测试。

答案 1 :(得分:2)

这是解决问题的一种方法,它使用用jq编写的非常简单的PRNG来随机选择名称进行替换 复制自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]] )

答案 2 :(得分:1)

假设您的area.json是有效的JSON,那么我相信以下内容将接近完成您的预期编辑:

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

但是,考虑到您提出的解决方案,对于我来说,“一维数组的随机值”的含义还不清楚。如果您是说应该随机选择索引(如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,并且希望为每次出现的.name字段从“名称”数组中随机选择一个值(使用替换?), ,那么一种选择是使用您喜欢的PRNG预先计算一个随机选择的名称数组(长度为features | length的数组),然后将该数组传递给jq:

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

另一种选择是使用用jq编写的PRNG,如本页其他位置所示。