JSON を簡単に動作させる 8 つの jq パターン

in tech

これらの jq フィルターは、選択、マップ、リデュース、並べ替え、デフォルト、および書式設定など、毎日行うジョブを処理します。これらの重要な機能の使用方法を説明と例で確認します。

プロパティ値に基づいてオブジェクトを選択する

一般的なデータベースのような操作であるこのパターンは、jq が SQL のエミュレーションにどれだけ近づくことができるかを示しています。オブジェクトのセットをフィルタリングし、プロパティ値に基づいて一部を取得する必要がある場合は、select 関数を使用します。

map(select(.property == "value"))

このフィルターは、オブジェクトが配列内にあり、その配列内にオブジェクトを保持したいことを前提としています。たとえば、次の入力があったとします。

( "fruits": map(.fruit) , "fruits": map(.fruit) )

このフィルターを使用して項目を選択できます。

map(select(.age == 10))
単一の一致するオブジェクトを結果として表示する jq select フィルター。

文字列をURIに変換する

jq は、文字列の書式設定やエスケープに使用できる、@ で始まる一連のフィルターをサポートしています。これらを使用して、CSV、HTML、base64、さらには URI 互換エンコーディングでファイルを生成できます。

@uri

このフィルターは、予約された各 URI 文字を %XX シーケンスにマッピングすることにより、パーセント エンコーディングを適用します。一般的な予約文字には、:、@、? が含まれます。

フィルターを使用するには、次のように文字列を渡すだけです。

jq '@uri' <<< '"Ask yourself: what can I do for my country?"'

結果の文字列には、元の予約文字ごとに %XX エンコーディングが含まれます。

jq @uri フィルターは、予約文字をエンコードされたシーケンスに変換します。

オブジェクトの構造を変換する

多くの jq タスクは、オブジェクトの変換 (データをある構造から別の構造に変換する) の範疇にあります。

マップ関数は、配列内の各値、またはオブジェクトの各プロパティに別の関数を適用します。これは、データをある形式から別の形式に変換するための完璧な汎用方法です。

この単純なデータを例として取り上げます。

(
     add ,
     add ,
    { "fruit": "apple", "price": 1 }
)

ここで、次の概要データを生成するとします。

{
    "fruits": ("apple", "banana"),
    "total": 4
}

まず、必要な出力を表す構造体に入力をマッピングします。

jq '{ "fruits": (), "total": 0 }' fruits.json

次に、元のデータからフェッチする必要がある各データに対してマップを使用します。

jq '{ "fruits": map(.fruit) | unique, "total": map(.price) | add }' fruits.json

セットを 1 つの値に減らす

Reduce は、プログラミング、特にデータベース操作やビッグ データ処理において非常に一般的な関数です。 jq では、reduce 構文を使用してセットを処理し、そこから単一の値を導出できます。一般的な構文は次のとおりです。

reduce expression as $var (init; update)

init 式は結果の初期値を定義しますが、update は $var を参照して結果を変更する式である必要があります。したがって、数値リストの合計を計算するには、次のようにします。

jq 'reduce .() as $item (0; . + $item)' <<< '(1,2,3)'

同様に、文字列のセットを 1 つの文字列に結合できます。

jq 'reduce .() as $item (""; . + $item + " ")' <<< '("hello","world")'

場合によっては、期待するほどきれいではないデータを操作する必要があることがあります。たとえば、数値が文字列に埋め込まれている場合、それを抽出してさらに処理することができます。

match 関数は正規表現検索を実行し、一致するそれぞれの値を出力します。文字列に対して実行すると、位置 (オフセット) や一致した文字列など、一致に関する情報を含むオブジェクトが返されます。

jq 一致フィルターは、オフセット、長さ、文字列などのプロパティを持つオブジェクトを返します。

したがって、これを使用して数値を抽出するには、次のようにアクセスできます。 string 結果からのプロパティ:

jq '. | match("\\d+") | .string' <<< '"1600 Pennsylvania Avenue NW"'

日付を柔軟にフォーマットする

jq は、一般的なデータ型に便利な関数を多数提供しています。そのような型の 1 つは date であり、ほとんどすべての日付書式設定ライブラリは、C 標準ライブラリの strftime 関数に依存しています。 jq には strftime 用の独自のインターフェイス ラッパーがあり、Unix エポック以降の現在時刻を取得する now のような関数もあります。

jq -n now
jq-now jq now フィルターは、Unix エポックからの秒数を表す大きな浮動小数点数を返します。

strftime 関数は、この関数の OS のバージョンがサポートするあらゆる形式を受け入れます。変換文字の例には、完全な年を表す %Y、エポックからの秒数を表す %s、曜日の完全名を表す %A などがあります。 %+ は便利な完全な日付と時刻の形式です。

jq -n 'now | strftime("%+")'
jq strftime フィルターは、%+ 変換で日付をフォーマットし、曜日とタイムゾーンを含む完全な日付と時刻を返します。

デフォルトのオブジェクトプロパティを設定する

データからオブジェクトを作成している場合、値が使用できない場合にデフォルトを指定すると便利です。このデータを例として取り上げます。

({
    "name": "pigeon",
    "type": "omnivore"
},{
    "name": "penguin",
    "type": "carnivore"
},{
    "name": "woodpecker"
})

デフォルト値を指定する標準的な方法では、if-then-else ブロックを使用してプロパティをチェックします。まだ存在しない場合、フィルターは固定のデフォルト値を使用して追加する必要があります。

jq < birds.json '.() | if (.type) then . else . + {"type": "omnivore"} end'

ここで、条件は type プロパティの存在をチェックします。存在する場合、ID フィルター (.) は単にオブジェクトをコピーします。ただし、そうでない場合は、+ 演算子を使用して、そのオブジェクトと、type プロパティのみを含むオブジェクトと、デフォルト値が結合されます。これにより、デフォルト値が適用された元のデータが得られます。

欠落しているオブジェクト プロパティのデフォルト値を提供するために使用される条件付きフィルター。

オブジェクトを完全に結合する

前の例では、+ 演算子を使用して 2 つのオブジェクトを結合しました。基本的なケースではこれで問題ありません。

jq -n '{ "foo": 1 } + { "bar": 42 }'

-n オプションは、入力の提供を要求するのではなく、null 入力を使用するように jq に指示するため、この種の演算子のテストに最適です。

ただし、+ 演算子は浅いマージのみを実行するため、より複雑なオブジェクトはこのアプローチでは失敗します。たとえば、次のフィルタでは完全なマージは生成されません。

{ "name": { "first": "John", "last": "Doe" } }
+
{ "name": { "first": "Jane" } }

代わりに、次のオブジェクトが返されます。

{
    "name": {
        "first": "Jane",
    }
}

それが理にかなっている場合もありますが、深いマージが必要な場合は、代わりに * 演算子を使用する必要があります。

* 演算子を使用して 2 つのオブジェクトに対してディープ マージを実行する jq フィルター。

関連記事

前の投稿
ペットのための快適ゾーンを設計する – The Dogington Post
次の投稿
これらの 5 つのスマートフォン機能は、誰も気づかないうちに大幅に向上しました。