如何基于第二个值和第三个值的出现次数设置JSON值

如何基于第二个值和第三个值的出现次数设置JSON值,json,linux,bash,jq,Json,Linux,Bash,Jq,初始files.json文档状态: [ { "filename": "a", "dir": "foo", "type": "unknown" }, { "filename": "b", "dir": "foo", "type": "unknown" }, { "filename": "c", "dir": "bar", "type": "unknown" }, { "filename":

初始
files.json
文档状态:

[
  {
    "filename": "a",
    "dir": "foo",
    "type": "unknown"
  },
  {
    "filename": "b",
    "dir": "foo",
    "type": "unknown"
  },
  {
    "filename": "c",
    "dir": "bar",
    "type": "unknown"
  },
  {
    "filename": "a",
    "dir": "bar",
    "type": "unknown"
  }
]
当“文件名”值在文档中仅出现一次时,“类型”值将仅取决于“目录”值:
“dir”==“foo”->“type”=0
“dir”==“bar”->“type”=1

当“foo”和“bar”目录中都存在“filename”值时,“type”的值必须等于2,如下所示:

[
  {
    "filename": "a",
    "dir": "foo",
    "type": "2"
  },
  {
    "filename": "b",
    "dir": "foo",
    "type": "0"
  },
  {
    "filename": "c",
    "dir": "bar",
    "type": "1"
  },
  {
    "filename": "a",
    "dir": "bar",
    "type": "2"
  }
]
“dir”值将始终限于“foo”或“bar”值的两个可能的备选值,并且永远不会为空。
甚至“filename”值也会被设置,因为这些文件实际上是文件,所以在文档中最多只能出现两次

我希望通过在Linux中运行Bash脚本的JQ1.5(2015年8月16日稳定版)来实现这一点

jq '
([.[] | { (.filename): {(.dir): true} }] | reduce .[] as $item ({}; . * $item)) as $seen |
map(
  if $seen[.filename]["foo"] and $seen[.filename]["bar"] then
    .type = 2
  elif $seen[.filename]["bar"] then
    .type = 1
  else
    .type = 0
  end
)
'

如果有助于理解其工作原理--
$seen
的值如下所示:

{
  "a": {
    "foo": true,
    "bar": true
  },
  "b": {
    "foo": true
  },
  "c": {
    "bar": true
  }
}
…这样,我们就可以方便地查看给定文件名使用过的目录条目


如果有助于理解其工作原理--
$seen
的值如下所示:

{
  "a": {
    "foo": true,
    "bar": true
  },
  "b": {
    "foo": true
  },
  "c": {
    "bar": true
  }
}

…因此,我们可以方便地查看给定文件名使用过的目录条目。

一种方法是使用
group\u by
。在下面的例子中,
group_by
被简单地使用,因此数组中最终结果的顺序由.filename决定;如果这不令人满意,可以使用相同的技术构造一个查找表,然后对原始数组使用该查找表

[group_by(.filename)[]
 | (map(.dir) 
    | unique
    | if length>1 then length
      elif .[0] == "foo" then 0
      elif .[0] == "bar" then 1
      else .[0]          # just in case
      end) as $type
    | (.[] + {type: $type} ) ]
输出
一种方法是使用
groupby
。在下面的例子中,
group_by
被简单地使用,因此数组中最终结果的顺序由.filename决定;如果这不令人满意,可以使用相同的技术构造一个查找表,然后对原始数组使用该查找表

[group_by(.filename)[]
 | (map(.dir) 
    | unique
    | if length>1 then length
      elif .[0] == "foo" then 0
      elif .[0] == "bar" then 1
      else .[0]          # just in case
      end) as $type
    | (.[] + {type: $type} ) ]
输出
这里有一个类似于@CharlesDuffy的解决方案,但略短一些,更“jq-ish”:

使用
catalog/3
借助通用辅助函数
catalog/3

def catalog(s; keyp; valuep):
   reduce s as $x ({}; (.[$x|keyp|tostring]) += [$x|valuep]);
解决方案变得更具可读性:

(catalog(.[]; .filename; .dir) | map_values(unique)) as $dirs
| map( $dirs[.filename] as $d
       | .type |= if $d|length > 1 then 2
                  elif $d[0] == "bar" then 1
                  else 0
                  end)

这里有一个类似于@CharlesDuffy的解决方案,但略短一些,更“jq-ish”:

使用
catalog/3
借助通用辅助函数
catalog/3

def catalog(s; keyp; valuep):
   reduce s as $x ({}; (.[$x|keyp|tostring]) += [$x|valuep]);
解决方案变得更具可读性:

(catalog(.[]; .filename; .dir) | map_values(unique)) as $dirs
| map( $dirs[.filename] as $d
       | .type |= if $d|length > 1 then 2
                  elif $d[0] == "bar" then 1
                  else 0
                  end)

@peak:事实上,文档有一个优先排序,它不是基于我希望能够保留的“文件名”,尽管这个过滤器仍然运行良好。但还有你的第二个解决方案,就像一首诗一样美丽。。。。我还有多少东西要学@peak:事实上,文档有一个优先排序,它不是基于我希望能够保留的“文件名”,尽管这个过滤器仍然运行良好。但还有你的第二个解决方案,就像一首诗一样美丽。。。。我还有多少东西要学!谢谢你,查尔斯,这是个好办法!是你激发了peak的第二个建议,它成为了我的最爱,因为它对我来说更“强大”;例如,as is不依赖于当前的“type”值,与这里不同(尽管我相信,在这里也可以很容易地获得)…如果您不想只更改当前
类型为
未知的
,那么只需删除
If
。谢谢@Charles,这是一个很好的解决方案!是你激发了peak的第二个建议,它成为了我的最爱,因为它对我来说更“强大”;例如,as is不依赖于当前的“type”值,与这里不同(尽管我相信,在这里也可以很容易地获得)…如果您不想只更改当前
类型为
未知的
,那么只需删除
If
。在我看来,这是一个完美的解决方案,在我看来,这似乎是一个完美的解决方案,一个让自己易于阅读和理解,并且工作出色的解决方案。