jq 为某些情况指定默认值

jq specify default value for some situation

现在我有一个 json

[
  {
    "city": "SH",
    "age": 0,
    "count": 1
  },
  {
    "city": "SH",
    "age": 20,
    "count": 1
  },
  {
    "city": "SH",
    "age": 40,
    "count": 1
  },
  {
    "city": "BJ",
    "age": 20,
    "count": 2
  },
  {
    "city": "BJ",
    "age": 30,
    "count": 1
  }
]

现在我想获取每个年龄段的计数数组以用于某些数据可视化目的(参见 demo)。例如

# age data: [shCount, bjCount]
age: 0, data:[1, 0]
age: 20, data: [1, 2] 
age: 30, data: [0, 1]
age: 40, data: [1, 0]

如果城市没有年龄,默认值0。但是如果城市没有年龄,它就没有相关的json对象。

所以我不能只使用下面的 shell 来获取每个年龄的计数数组,例如

# without BJ value
➜  ~ jq -c '.[] | select(.age==0) | [.city, .count]' foo.json
["SH",1]

➜  ~ jq -c '.[] | select(.age==20) | [.city, .count]' foo.json
["SH",1]
["BJ",2]

那么如果age为0,如何给BJ指定一个默认值0?

为清楚起见,合并同龄记录的辅助函数很有用。这是定义默认值的地方:

def merge:
  reduce .[] as $x ([0,0];
    if $x.city == "SH" then .[0] = $x.count else .[1] = $x.count end);

接下来,我们只需按年龄对记录进行分组:

group_by(.age)
| map( {age: .[0].age, data: merge} )

调用:

jq -c -f program.jq input.json

结果:

[{"age":0,"data":[1,0]},{"age":20,"data":[1,2]},{"age":30,"data":[0,1]},{"age":40,"data":[1,0]}]

然后您可以按照您想要的方式格式化结果。

如果事先不知道相关的城市名称,那么如果城市名称是字符串,则以下方法值得推荐:

# Merge records having the same age
def merge:
  reduce .[] as $x ({}; . + ($x | { (.city) : .count}) );

# Create an object holding the default values:
def zeros:
  map( {(tostring): 0} ) | add;

group_by(.age)
| map( {age: .[0].age, data: merge} )
| (map(.data) | add | keys | zeros) as $zeros
| map( .data = $zeros + .data )

上面的输出是具有以下形式的对象数组: {"age":_,"data":城市}

例如

 {
    "age": 40,
    "data": {
      "BJ": 0,
      "SH": 1
    }
  } 

现在可以轻松地将 CITIES 转换为所需的格式。