jqを使ってjson配列要素の位置を条件に変更するには?

jqを使ってjson配列要素の位置を条件に変更するには?

条件に応じて配列要素の位置を変更したい(配列要素のインデックスの変更)。これをjqに翻訳する方法がわかりません。これは次のとおりです。機能の言語。
基本的に配列をソートしたいのですが比較的特定の要素の位置は変更されていないままにしてください。

for each element:
if element.role==master => record type
  for each element:
    if element.type == recorded type
      reposition the element to be below its master of similar type 

たとえば、よりよく説明できます。考慮してくださいinput.json。 「x」型のすべての非主要素を主要素の下に移動する方法いいえ変化比較的同じタイプの非マスター状態です。 ("num"引数は無視されます。相対性を示すためにのみ使用されます。)

入力.json

[
    {
        "type": "A",
        "role": "master"
    },
    {
        "num": 1,
        "type": "A"
    },
    {
        "type": "C",
        "role": "master"
    },
    {
        "num": 4,
        "type": "B"
    },
    {
        "num": 2,
        "type": "B"
    },
    {
        "type": "B",
        "role": "master"
    },
    {
        "num": 3,
        "type": "B"
    },
    {
        "num": 4,
        "type": "A"
    },
    {
        "num": 2,
        "type": "A"
    },
    {
        "num": 0,
        "type": "C"
    },
    {
        "num": 5,
        "type": "C"
    },
    {
        "num": 1,
        "type": "A"
    },
    {
        "num": 1,
        "type": "B"
    }
]

ターゲット.json

[
    {
        "type": "A",
        "role": "master"
    },
    {
        "num": 1,
        "type": "A"
    },
    {
        "num": 4,
        "type": "A"
    },
    {
        "num": 2,
        "type": "A"
    },
    {
        "num": 1,
        "type": "A"
    },
    {
        "type": "C",
        "role": "master"
    },
    {
        "num": 0,
        "type": "C"
    },
    {
        "num": 5,
        "type": "C"
    },
    {
        "type": "B",
        "role": "master"
    },
    {
        "num": 4,
        "type": "B"
    },
    {
        "num": 2,
        "type": "B"
    },
    {
        "num": 3,
        "type": "B"
    },
    {
        "num": 1,
        "type": "B"
    }
]

ご覧のとおり、
1-マスターの相対位置は同じままです(A - C - B)。
2-同じタイプの非マスターの相対位置は同じままです。

(この問題はアルゴリズム文献に名前があると思いますか?内部ソート?)

答え1

方法:

  1. マスターリストを取得し、そのタイプを抽出します。この順序セットは、残りのデータをどの順序で処理するかを示します。

    ( .[] | select(.role == "master").type )
    

    与えられたデータに対して、これは集合"A"、、"C"です"B"

  2. このコレクションを繰り返して、マスターロールを持つそのタイプの要素を抽出し、そのタイプはあるがマスターロールを持たない要素を抽出します。

    ループは$typeループ変数として実行されます。

    ( .[] | select(.role == "master").type ) as $type
    

    マスターを抽出してから非マスターを抽出します。

    ( .[] | select(.type == $type and .role == "master" ) ), 
    ( .[] | select(.type == $type and .role != "master" ) )
    
  3. すべてを配列に入れます。これには、すべてを配置し[]囲むことが含まれます。

私たちはついに得ます

[
    ( .[] | select(.role == "master").type ) as $type |
    ( .[] | select(.type == $type and .role == "master" ) ), 
    ( .[] | select(.type == $type and .role != "master" ) )
]

実際、ここではソートは進行しません。私たちは順番にデータを抽出し、それから新しい配列を作成します。


回避策:まず、その型だけでなく、配列から主要な要素全体を抽出します。

[
    ( .[] | select(.role == "master") ) as $master |
    $master,
    ( .[] | select(.type == $master.type and .role != "master" ) )
]

別のアプローチ:まず、最初の配列のグループ化を使用して、基本要素を他の要素から分離します。これは、役割を持つ要素のみが存在するか、またはmaster役割がまったくないと仮定します。

group_by(.role) |
[
    .[1][] as $master |
    $master,
    ( .[0][] | select(.type == $master.type )
]

最初の行は、元の配列を.[0]役割のない要素と.[1]役割のある要素を含む2つの部分に分割することから始まります。

次にinのキー要素を繰り返して、現在のタイプに対応する要素を選択します.[1].[0]$master

各基本要素の後に非基本要素が続く配列を作成します。

関連情報