Sort

如何使用 jq 有條件地重新定位 json 數組的元素?

  • July 9, 2021

我想根據條件重新定位數組的元素(更改數組元素的索引)。我不知道如何將它翻譯成 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- master 的相對位置保持不變(A - C - B)

2- 相同類型的 non-master 的相對位置保持不變。

(我猜這個問題在算法文獻中有名字?就地排序?)

方法:

  1. 獲取 master 列表並提取它們的類型。這個有序集告訴我們以什麼順序處理其餘數據。
( .[] | 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" ) )
  1. 將所有內容放入數組中。這涉及到放置所有[東西]

我們最終得到

[
   ( .[] | 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]

然後我們遍歷主元素.[1]並從中選擇與.[0]目前$master類型相對應的元素。

創建一個數組,其中每個主元素依次跟隨非主元素。

引用自:https://unix.stackexchange.com/questions/657721