【问题标题】:How to conditionally select array index to update based on value如何根据值有条件地选择要更新的数组索引
【发布时间】:2020-10-05 17:32:31
【问题描述】:

我有一个如下的json文件,需要将新名称附加到.root.application.names,但是如果传入的名称有前缀(-之前的所有内容,在下面的示例中是jr),然后找到列表已经存在相同的前缀名称,并更新它,如果只有一个名称列表或没有匹配的列表,则更新第一个列表。

在下面的例子中,如果

$application == 'application1' and $name == <whatever>; just update first list under application1, as there is only one list under application1, nothing to choose from.

$application == 'application2' and if $name has no prefix delimiter "-" or unmatched prefix (say sr-allen); then update the first list under application2.names, because foo has no or unmatched prefix.

$application == 'application2' and say $name == jr-allen; then update the second list under application2, because $name has prefix "jr-" and there is a list with items matching this prefix.

{
    "root": {
        "application1": [
            {
                "names": [
                    "john"
                ],
                "project": "generic"
            }
        ],
        "application2": [
            {
                "names": [
                    "peter",
                    "jack"
                ],
                "project": "generic"
            },
            {
                "names": [
                    "jr-sam",
                    "jr-mike",
                    "jr-rita"
                ],
                "project": "junior-project"
            }
         ]
     }
}

我找到了如何更新列表,不知道如何添加这些条件,请帮助?

jq '."root"."application2"[1].names[."root"."application2"[1].names| length] |= . + "jr-allen"' foo.json

更新: 如果我能用 jq/walk 做到这一点很好,我仍在尝试如下,但无法接近。

prefix=$(echo ${name} | cut -d"-" -f1) # this gives the prefix, e.g: "jr"
jq -r --arg app "${application}" name "${name}" prefix "${prefix}"'
    def walk(f):
    . as $in
    | if type == "object" then
        reduce keys[] as $key
            ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
    elif type == "array" then map( walk(f) ) | f
    else f
    end;
  walk( if type=="object" and ."$app" and (.names[]|startswith("$prefix")) ) then .names[]="$name" else . end )
' foo.json

【问题讨论】:

    标签: json shell select jq


    【解决方案1】:

    我花了一段时间才确信自己已经理解了这些要求,但我相信以下内容至少抓住了您所想的精髓。

    为了使解决方案更容易理解,我们从一个辅助函数开始,它的名称使其目的足够清晰,至少在上下文中是这样:

    def updateFirstNamesArrayWithMatchingPrefix($prefix; $value):
      (first( range(0; length) as $i
              | if any(.[$i].names[]; startswith($prefix))
                then $i else empty end) // 0) as $i
      | .[$i].names += [$value] ;
    
      
    .root |=
      if .[$app] | length == 1 
      then .[$app][0].names += [ $name ]
      elif .[$app] | length > 1
      then 
        ( $name | split("-")) as $components
        | if $components|length==1  # no prefix
          then .[$app][0].names += [ $name  ]
          else ($components[0] + "-" ) as $prefix
          | .[$app] |= updateFirstNamesArrayWithMatchingPrefix($prefix; $name)
          end
      else .
      end
    

    测试

    以上通过了最初提出的四个测试 @伊尼安:

    jq --arg app "application1" --arg name "foo" -f script.jq jsonFile
    jq --arg app "application2" --arg name "jr-foo" -f script.jq jsonFile
    jq --arg app "application2" --arg name "sr-foo" -f script.jq jsonFile
    jq --arg app "application2" --arg name "foo" -f script.jq jsonFile
    

    鲱鱼?

    根据我对问题的理解,在我看来walk可能有点牵强,但如果不是,我希望您能够根据您的实际要求调整上述内容。

    【讨论】:

    • 效果很好,谢谢,有没有办法使输出un_sorted?我是使用 jq 的初学者,如果这是一个基本的东西,对不起。
    • 我用的是jq-1.5版本,不知道上面函数中keys_unsorted/0怎么用
    • jq程序没有做任何排序,我不明白你的问题。
    【解决方案2】:

    您的要求是:如果名称中存在前缀,则更新最后一个数组元素,否则更新第一个。

    当数组有一个元素时,比如application1,最后一个也是第一个。

    #!/bin/bash
    
    application="$1"
    name="$2"
    json_file="file.json"
    ind=0
    
    # if name matches "-", set index to the last array element
    [[ "$name" == *"-"* ]] && ind=-1
    
    jq --arg app "$application" \
       --arg name "$name" \
       --argjson ind "$ind" \
       '.root[$app][$ind].names += [$name]' "$json_file"
    

    我相信上面的脚本是不言自明的,--argjson 用于有一个不带引号的索引,+= 代表|= . +


    测试

    下面的命令会产生预期的结果。

    bash test.sh application1 jr-John
    bash test.sh application2 jr-John
    bash test.sh application1 Mary
    bash test.sh application2 Mary
    

    【讨论】:

    • 感谢@thanasisp,但如果我有超过 2 个具有不同前缀的列表,这将不起作用。感谢您的回复。
    • 显然,正如您的描述和您的示例所说,对于每个对象,都有一个带有名称的数组,一个带有前缀名称的数组。如果您有许多具有许多不同前缀的数组,那么如果您将其包含在您的示例案例中会很棒。
    猜你喜欢
    • 2018-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-04
    • 1970-01-01
    • 2021-11-17
    • 2016-11-06
    相关资源
    最近更新 更多