次のようなcsvデータセットがある場合:
name, age, gender
john, 20, male
jane, 30, female
bob, 25, male
これに到達できますか:
[ {"name": "john", "age": 20, "gender: "male"},
{"name": "jane", "age": 30, "gender: "female"},
{"name": "bob", "age": 25, "gender: "male"} ]
jqのみを使用していますか?
私は this 記事を見つけましたが、これは私がやろうとしていることを示していますが、ヘッダーフィールドと値の「手動」マッピングを使用しています。ヘッダーフィールドの名前を変更する必要はありません。また、レイアウトが変わるたびにスクリプトやコマンドを変更する必要もありません。
ヘッダーを動的に抽出し、jqワンライナーで値と結合することは可能ですか?
要するに-はい、多分ワンライナービットを除いて。
jqはテキストのラングリングに適していることが多く、これは特に正規表現をサポートするバージョンに当てはまります。たとえば、正規表現のサポートを使用すると、特定の問題ステートメントで必要なトリミングは簡単です。
Jq 1.5rc1は正規表現サポートを含み、2015年1月1日から利用可能であるため、次のプログラムはjq 1.5のバージョンを想定しています。 jq 1.4で動作させる場合は、2つの「For jq 1.4」コメントを参照してください。
また、このプログラムはCSVの一般性と複雑さのすべてを処理するわけではないことにも注意してください。 (CSVをより一般的に処理する同様のアプローチについては、 https://github.com/stedolan/jq/wiki/Cookbook#convert-a-csv-file-with-headers-to-json を参照してください=)
# objectify/1 takes an array of string values as inputs, converts
# numeric values to numbers, and packages the results into an object
# with keys specified by the "headers" array
def objectify(headers):
# For jq 1.4, replace the following line by: def tonumberq: .;
def tonumberq: tonumber? // .;
. as $in
| reduce range(0; headers|length) as $i ({}; .[headers[$i]] = ($in[$i] | tonumberq) );
def csv2table:
# For jq 1.4, replace the following line by: def trim: .;
def trim: sub("^ +";"") | sub(" +$";"");
split("\n") | map( split(",") | map(trim) );
def csv2json:
csv2table
| .[0] as $headers
| reduce (.[1:][] | select(length > 0) ) as $row
( []; . + [ $row|objectify($headers) ]);
csv2json
例(csv.csvが指定のCSVテキストファイルであると想定):
$ jq -R -s -f csv2json.jq csv.csv
[
{
"name": "john",
"age": 20,
"gender": "male"
},
{
"name": "jane",
"age": 30,
"gender": "female"
},
{
"name": "bob",
"age": 25,
"gender": "male"
}
]
miller( http://johnkerl.org/miller/doc/ )を使用すると非常に簡単です。このinput.csvファイルを使用する
name,age,gender
john,20,male
jane,30,female
bob,25,male
と実行
mlr --c2j --jlistwrap cat input.csv
あなたは
[
{ "name": "john", "age": 20, "gender": "male" }
,{ "name": "jane", "age": 30, "gender": "female" }
,{ "name": "bob", "age": 25, "gender": "male" }
]
私は少し遊びをして、これを思いつきました。しかし、それは最善の方法ではないかもしれません、あなたの試みがどのようなものであったかを見てみたいと思います両方が解決策にたどり着いたら、それは2倍良いと確信しています!
しかし、私は次のようなものから始めます:
_true as $doHeaders
| . / "\n"
| map(. / ", ")
| (if $doHeaders then .[0] else [range(0; (.[0] | length)) | tostring] end) as $headers
| .[if $doHeaders then 1 else 0 end:][]
| . as $values
| keys
| map({($headers[.]): $values[.]})
_
変数_$doHeaders
_は、先頭行をヘッダー行として読み取るかどうかを制御します。あなたの場合、あなたはそれを本当のようにしたいのですが、私は将来のためにそれを追加しましたSO usersそして、まあ、今日私は素晴らしい朝食を食べました、そして天気が良いので、なぜですか?
少し説明:
1)_. / "\n"
_行で分割...
2)map(. / ", ")
...とコンマ(Big gotcha:ご使用のバージョンでは、正規表現ベースの分割を使用する必要がありますこのように、引用符で囲まれたカンマで分割するからです。簡潔にするためにこれを使用しましたが、それによって私のソリューションはクールに見えますか?)
3)_if $doHeaders then...
_ここでは、最初の行の要素の数と最初の行がヘッダー行かどうかに応じて、キーまたは数値の文字列の配列を作成します
4)_.[if $doHeaders then 1 else 0 end:]
_わかりました。ヘッダーの場合、一番上の行を削除してください
5)map({($headers[.]): $values[.]})
上記では、以前のcsvの各行を調べ、_$values
_を変数に入れ、キーをパイプに入れました。次に、目的のオブジェクトを作成します。
もちろん、いくつかの正規表現を使って問題点を記入する必要がありますが、それが途中で始まることを願っています。
2018年以降、コードなしの最新のソリューションはPython tool csvkit
has csvjson data.csv > data.json
。
ドキュメントを参照 https://csvkit.readthedocs.io/en/1.0.2/
このツールキットは、スクリプトがjq
とcsv
の両方の形式をデバッグする必要がある場合にも、非常に便利でjson
を補完します。
visidata と呼ばれる強力なツールを確認することもできます。これが スクリーンキャストケーススタディ で、元のポスターに似ています。 visidata
からスクリプトを生成することもできます
以下は、-s
および-R
オプションを指定してjqを実行することを想定したソリューションです。
[
[
split("\n")[] # transform csv input into array
| split(", ") # where first element has key names
| select(length==3) # and other elements have values
]
| {h:.[0], v:.[1:][]} # {h:[keys], v:[values]}
| [.h, (.v|map(tonumber?//.))] # [ [keys], [values] ]
| [ transpose[] # [ [key,value], [key,value], ... ]
| {key:.[0], value:.[1]} # [ {"key":key, "value":value}, ... ]
]
| from_entries # { key:value, key:value, ... }
]
サンプルの実行:
jq -s -R -f filter.jq data.csv
出力例
[
{
"name": "john",
"age": 20,
"gender": "male"
},
{
"name": "jane",
"age": 30,
"gender": "female"
},
{
"name": "bob",
"age": 25,
"gender": "male"
}
]