链接 JQ if: if A then B C else D end
Chaining JQ if: if A then B C else D end
我想在 JQ if 语句中交换两个值。
给出以下 JSON:
{
"food": {
"fruit": [
{
"type": "apple",
"count": 0
},
{
"type": "banana",
"count": 1
},
{
"type": "orange",
"count": 0
}
]
}
}
我想使用 JQ 根据条件交换两个对象的计数以产生以下结果。
{
"food": {
"fruit": [
{
"type": "apple",
"count": 1
},
{
"type": "banana",
"count": 0
},
{
"type": "orange",
"count": 0
}
]
}
}
到目前为止我可以用
将第一个水果从0改成1
jq '
if .food.fruit[] | select(.type=="apple") | .count == 0
then
.food.fruit[] | select(.type=="apple") | .count = 1
else
empty
end
'
但我找不到正确的运算符来修改第二个。使用 JQ 是否可以实现类似以下内容?
jq '
if .food.fruit[] | select(.type=="apple") | .count == 0
then
.food.fruit[] | select(.type=="apple") | .count = 1 &
.food.fruit[] | select(.type=="banana") | .count = 0
else
empty
end
'
我无法将其通过管道传输,因为那样会将单个水果对象通过管道传输到下一行,所以我不确定我应该在此处使用哪个运算符 - 如果甚至支持此类功能。
非常感谢任何帮助!
.food.fruit |= (
( .[] | select( .type == "apple" ) | .count ) as $count1 |
( .[] | select( .type == "banana" ) | .count ) as $count2 |
( .[] | select( .type == "apple" ) | .count ) = $count2 |
( .[] | select( .type == "banana" ) | .count ) = $count1
)
.food.fruit |= (
( .[] | select( .type == "apple" ) | .count ) as $count1 |
( .[] | select( .type == "banana" ) | .count ) as $count2 |
.[] |= (
.count = (
if .type == "apple" then
$count2
elif .type == "banana" then
$count1
else
.count
end
)
)
)
这是一个高效的解决方案,甚至可能不需要完全遍历 fruit
数组一次。
想法是找到“apple”和“banana”对象的路径,这样交换就可以在没有任何进一步迭代的情况下完成。
为了提高效率,该解决方案使用 foreach
以便在找到“苹果”和“香蕉”对象的路径后跳出(单个)循环。
.food.fruit |=
( . as $fruits
| label $go
| (foreach paths(objects) as $p ({};
($fruits|getpath($p)) as $x
| if ($x|.type)=="apple"
then .apple = {path: ($p + ["count"]), count: $x.count}
elif ($x|.type) == "banana"
then .banana = {path: ($p + ["count"]), count: $x.count}
else . end;
if .apple and .banana then ., break $go else empty end)
) as $dict
| if $dict | .apple and .banana
then setpath( $dict.apple.path; $dict.banana.count)
| setpath( $dict.banana.path; $dict.apple.count)
else . end
)
def swap/4
上述方案可以抽象为:
# Input: an array of JSON objects.
#
# In the objects with .[$f1key] == $f1 and .[$f1key] == $f2 if any,
# swap the values at .[$gkey].
# If the the array has more than one object with .[$f1key] == $f1,
# then the last one will be selected, and similarly with respect to $f2
#
def swap($f1; $f2; $fkey; $gkey):
. as $in
| label $go
| (foreach paths(objects) as $p ({};
($in|getpath($p)) as $x
| if ($x[$fkey])==$f1
then .f1 = {path: ($p + [$gkey]), count: $x[$gkey]}
elif ($x[$fkey]) == $f2
then .f2 = {path: ($p + [$gkey]), count: $x[$gkey]}
else . end;
if .f1 and .f2 then ., break $go else empty end)
) as $dict
| if $dict | .f1 and .f2
then setpath( $dict.f1.path; $dict.f2.count)
| setpath( $dict.f2.path; $dict.f1.count)
else . end ;
.food.fruit |= swap("apple"; "banana"; "type"; "count")
这是一个简单的解决方案,它仅使用内置函数并展示了如何使用“|”将更新链接在一起(*).
.food.fruit |=
(map(.type) as $types
| ($types | index("apple")) as $ia
| ($types | index("banana")) as $ib
| .[$ia].count as $ac
| .[$ia].count = .[$ib].count
| .[$ib].count = $ac
)
更高效的解决方案
这是一个类似的解决方案,它避免了 index
的问题,并且由于不使用 map
.
而更加 space 高效
# index of $needle in a stream
def index_of($needle; stream):
label $go
| foreach stream as $x (-1; .+1; select($x==$needle) | (., break $go))
// null;
.food.fruit |=
( index_of("apple"; .[].type) as $ia
| index_of("banana"; .[].type) as $ib
| .[$ia].count as $ac
| .[$ia].count = .[$ib].count
| .[$ib].count = $ac
)
(*)
内置的 index
(在 jq 1.6 和更早版本中)没有有效地实现,虽然是用 C 编写的,但通常速度很快。这是一个算法上有效的实现:
def ix($x):
label $go
| foreach .[] as $v (-1; .+1; select($v == $x) | (., break $go))
// null;
我想在 JQ if 语句中交换两个值。
给出以下 JSON:
{
"food": {
"fruit": [
{
"type": "apple",
"count": 0
},
{
"type": "banana",
"count": 1
},
{
"type": "orange",
"count": 0
}
]
}
}
我想使用 JQ 根据条件交换两个对象的计数以产生以下结果。
{
"food": {
"fruit": [
{
"type": "apple",
"count": 1
},
{
"type": "banana",
"count": 0
},
{
"type": "orange",
"count": 0
}
]
}
}
到目前为止我可以用
将第一个水果从0改成1jq '
if .food.fruit[] | select(.type=="apple") | .count == 0
then
.food.fruit[] | select(.type=="apple") | .count = 1
else
empty
end
'
但我找不到正确的运算符来修改第二个。使用 JQ 是否可以实现类似以下内容?
jq '
if .food.fruit[] | select(.type=="apple") | .count == 0
then
.food.fruit[] | select(.type=="apple") | .count = 1 &
.food.fruit[] | select(.type=="banana") | .count = 0
else
empty
end
'
我无法将其通过管道传输,因为那样会将单个水果对象通过管道传输到下一行,所以我不确定我应该在此处使用哪个运算符 - 如果甚至支持此类功能。 非常感谢任何帮助!
.food.fruit |= (
( .[] | select( .type == "apple" ) | .count ) as $count1 |
( .[] | select( .type == "banana" ) | .count ) as $count2 |
( .[] | select( .type == "apple" ) | .count ) = $count2 |
( .[] | select( .type == "banana" ) | .count ) = $count1
)
.food.fruit |= (
( .[] | select( .type == "apple" ) | .count ) as $count1 |
( .[] | select( .type == "banana" ) | .count ) as $count2 |
.[] |= (
.count = (
if .type == "apple" then
$count2
elif .type == "banana" then
$count1
else
.count
end
)
)
)
这是一个高效的解决方案,甚至可能不需要完全遍历 fruit
数组一次。
想法是找到“apple”和“banana”对象的路径,这样交换就可以在没有任何进一步迭代的情况下完成。
为了提高效率,该解决方案使用 foreach
以便在找到“苹果”和“香蕉”对象的路径后跳出(单个)循环。
.food.fruit |=
( . as $fruits
| label $go
| (foreach paths(objects) as $p ({};
($fruits|getpath($p)) as $x
| if ($x|.type)=="apple"
then .apple = {path: ($p + ["count"]), count: $x.count}
elif ($x|.type) == "banana"
then .banana = {path: ($p + ["count"]), count: $x.count}
else . end;
if .apple and .banana then ., break $go else empty end)
) as $dict
| if $dict | .apple and .banana
then setpath( $dict.apple.path; $dict.banana.count)
| setpath( $dict.banana.path; $dict.apple.count)
else . end
)
def swap/4
上述方案可以抽象为:
# Input: an array of JSON objects.
#
# In the objects with .[$f1key] == $f1 and .[$f1key] == $f2 if any,
# swap the values at .[$gkey].
# If the the array has more than one object with .[$f1key] == $f1,
# then the last one will be selected, and similarly with respect to $f2
#
def swap($f1; $f2; $fkey; $gkey):
. as $in
| label $go
| (foreach paths(objects) as $p ({};
($in|getpath($p)) as $x
| if ($x[$fkey])==$f1
then .f1 = {path: ($p + [$gkey]), count: $x[$gkey]}
elif ($x[$fkey]) == $f2
then .f2 = {path: ($p + [$gkey]), count: $x[$gkey]}
else . end;
if .f1 and .f2 then ., break $go else empty end)
) as $dict
| if $dict | .f1 and .f2
then setpath( $dict.f1.path; $dict.f2.count)
| setpath( $dict.f2.path; $dict.f1.count)
else . end ;
.food.fruit |= swap("apple"; "banana"; "type"; "count")
这是一个简单的解决方案,它仅使用内置函数并展示了如何使用“|”将更新链接在一起(*).
.food.fruit |=
(map(.type) as $types
| ($types | index("apple")) as $ia
| ($types | index("banana")) as $ib
| .[$ia].count as $ac
| .[$ia].count = .[$ib].count
| .[$ib].count = $ac
)
更高效的解决方案
这是一个类似的解决方案,它避免了 index
的问题,并且由于不使用 map
.
# index of $needle in a stream
def index_of($needle; stream):
label $go
| foreach stream as $x (-1; .+1; select($x==$needle) | (., break $go))
// null;
.food.fruit |=
( index_of("apple"; .[].type) as $ia
| index_of("banana"; .[].type) as $ib
| .[$ia].count as $ac
| .[$ia].count = .[$ib].count
| .[$ib].count = $ac
)
(*)
内置的 index
(在 jq 1.6 和更早版本中)没有有效地实现,虽然是用 C 编写的,但通常速度很快。这是一个算法上有效的实现:
def ix($x):
label $go
| foreach .[] as $v (-1; .+1; select($v == $x) | (., break $go))
// null;