如何有条件地选择要基于值更新的数组索引 [英] How to conditionally select array index to update based on value

查看:70
本文介绍了如何有条件地选择要基于值更新的数组索引的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个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

解决方案

我花了一点时间对我已经理解的要求充满信心,但是我相信以下内容至少可以反映出您所想到的本质.

为了使解决方案更容易理解,我们从一个辅助函数开始,该函数的名称至少在上下文中明确了其目的:

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

测试

以上内容通过了原先提出的四项测试 @Inian:

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可能有点像是鲱鱼,但是如果不是,我希望您能够对以上内容进行调整以满足您的实际需求. /p>

I have a json file as below, need to append the new name into .root.application.names, but if the name passed in has prefix (everything before -, in below example it's jr), then find the list with same prefix names already present, and update it, if there is only one names list or if there is no matching list, then update first list.

In the below example, if

$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"
            }
         ]
     }
}

I found how to update the list, not sure how to add these conditions, any help please?

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

Update: good if I can do this with jq/walk, I am still trying as below, but couldn't get anywhere close.

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

解决方案

It took me a while to have any confidence that I have understood the requirements, but I believe the following at least captures the essence of what you have in mind.

To make the solution easier to understand, we begin with a helper function, the name of which makes its purpose clear enough, at least given the context:

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

Testing

The above passes the four tests originally proposed by @Inian:

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

Herrings?

Based on my understanding of the problem, it seems to me that walk may be a bit of a red herring, but if not, I hope you'll be able to adapt the above to meet your actual requirements.

这篇关于如何有条件地选择要基于值更新的数组索引的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆