如何根据第二个值以及第三个值的出现次数设置JSON值

时间:2018-08-04 10:13:04

标签: 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": "a",
    "dir": "bar",
    "type": "unknown"
  }
]

当“ filename”值仅在文档中出现一次时,“ type”值将仅取决于“ dir”值:"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”值的两个可能的选择,并且永远不会为null。
甚至将设置“ filename”值,并且由于这些文件实际上是文件,因此在文档中最多只能出现两次。

我想通过从Linux中的Bash脚本运行的jq 1.5(2015年8月16日稳定版)来实现这一点。

3 个答案:

答案 0 :(得分:3)

这里的解决方案与@CharlesDuffy的解决方案类似,但更简短,更“ jq-ish”:

(reduce .[] as $x ({}; .[$x.filename] += [$x.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)

使用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)

答案 1 :(得分:2)

一种方法是使用group_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} ) ]

输出

[
  {
    "filename": "a",
    "dir": "foo",
    "type": 2
  },
  {
    "filename": "a",
    "dir": "bar",
    "type": 2
  },
  {
    "filename": "b",
    "dir": "foo",
    "type": 0
  },
  {
    "filename": "c",
    "dir": "bar",
    "type": 1
  }
]

答案 2 :(得分:1)

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
  }
}

...因此,我们可以轻松查看给定文件名的哪个目录条目。