如何使用 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. 循环此集合并提取具有该类型且具有 master 角色的元素,然后提取具有该类型但不具有 master 角色的元素。

    循环与$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]具有角色的元素。

然后,我们循环遍历 in 的主元素并从中选择与当前类型相对应的.[1]元素。.[0]$master

创建一个数组,其中每个主元素依次跟随非主元素。

相关内容