Es gibt viele ähnliche Fragen, aber keine zum dynamischen Verbinden von 2 Dateien. Ich versuche, die folgende Struktur dynamisch zu bearbeiten:

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

Ich möchte nur das eine Feld .features[].properties.name durch einen zufälligen Wert aus einem 1d-Array in einer anderen txt-Datei ersetzen. Es gibt 8.000 Funktionen und ungefähr 100 Namen, die ich vorbereitet habe.

Folgendes habe ich jetzt mit Fehlern versagt:

#!/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

Ist es überhaupt möglich, einen einzigen Befehl auszuführen, oder sollte ich für solche Aufgaben Python oder js verwenden?

1
Igniter 17 Jän. 2019 im 21:07

3 Antworten

Beste Antwort

Ihr Dokument (https://echarts.baidu.com/examples /data-gl/asset/data/buildings.json) ist eigentlich klein genug, dass wir keine verrückten Tricks zur Speichererhaltung ausführen müssen, damit es funktioniert. Die folgenden Funktionen sind unverändert:

# 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 .)

Dies funktioniert, weil shuf -r words.txt einen endlosen Strom von Wörtern erstellt, die zufällig aus words.txt ausgewählt wurden, und das jq -R . innerhalb der Prozessersetzung diese als Zeichenfolgen zitiert. (Da wir input nur einmal pro Element in buildings.json aufrufen, versuchen wir nicht, weiterzumachen, nachdem der Inhalt dieser Datei vollständig verbraucht wurde.)


Für das winzige Dokument mit zwei Datensätzen in der Frage sieht die Ausgabe folgendermaßen aus:

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

... wobei die tatsächlichen Wörter bei jedem Lauf variieren; Es wurde in ähnlicher Weise mit der vollständigen extern gehosteten Datei getestet.

2
Charles Duffy 17 Jän. 2019 im 23:15

Hier ist eine Lösung für das Problem der zufälligen Auswahl der Namen durch Ersetzen unter Verwendung des sehr einfachen PRNG, das in jq geschrieben ist kopiert von https://rosettacode.org/wiki/Random_numbers#jq

Aufruf:

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

Bereiche.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
peak 17 Jän. 2019 im 23:35

Angenommen, Ihre area.json ist gültiges JSON, dann würde das Folgende meiner beabsichtigten Bearbeitung nahe kommen:

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

Angesichts Ihrer vorgeschlagenen Lösung ist mir jedoch nicht klar, was Sie unter einem "Zufallswert aus einem 1d-Array" verstehen. Wenn Sie meinen, dass der Index zufällig ausgewählt werden soll (wie von einem PRNG), würde ich vorschlagen, ihn mit Ihrem bevorzugten PRNG zu berechnen und diesen zufälligen Wert als weiteres Argument an jq zu übergeben, wie im folgenden Abschnitt dargestellt.

So stellt sich die Frage, wie der Text transformiert werden kann

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

In ein gültiges JSON-Array. Es gibt zahlreiche Möglichkeiten, dies zu tun, ob mit jq oder nicht, aber ich glaube, dass dies am besten als separate Frage oder als Übung bleibt, da die Auswahl der Methode wahrscheinlich von bestimmten Details abhängt, die in diesem Q nicht erwähnt werden Persönlich würde ich wenn möglich sed verwenden; Sie können auch verwenden, wie auch in dargestellt der folgende Abschnitt.

Illustration mit hjson und 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

Nachtrag

Derzeit verfügt jq nicht über ein integriertes PRNG. Wenn Sie jedoch jq verwenden möchten und einen Wert aus dem Array "names" zufällig (mit Ersetzung?) Für jedes Vorkommen des Felds .name auswählen möchten, dann einen Die Option besteht darin, ein Array der zufällig ausgewählten Namen (ein Array mit der Länge features | length) unter Verwendung Ihres bevorzugten PRNG vorab zu berechnen und dieses Array an jq zu übergeben:

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

Eine andere Möglichkeit wäre die Verwendung eines in jq geschriebenen PRNG, wie an anderer Stelle auf dieser Seite dargestellt.

1
peak 18 Jän. 2019 im 00:33