简体   繁体   English

jq:从数组中删除与基于同一数组中其他项的条件匹配的项

[英]jq: Remove items from array that match a condition based on other items in the same array

I need help with a somewhat complex jq query. 我需要一些有点复杂的jq查询的帮助。

Given 特定

[{
  "type": "ITEM_PURCHASED",
  "timestamp": 1710829,
  "participantId": 2,
  "itemId": 3089
},
{
  "type": "ITEM_PURCHASED",
  "timestamp": 1711620,
  "participantId": 7,
  "itemId": 2055
},
{
  "type": "ITEM_PURCHASED",
  "timestamp": 1711621,
  "participantId": 7,
  "itemId": 1058
},
{
  "type": "ITEM_PURCHASED",
  "timestamp": 1714435,
  "participantId": 9,
  "itemId": 1037
},
{
  "type": "ITEM_UNDO",
  "timestamp": 1716107,
  "participantId": 7,
  "afterId": 0,
  "beforeId": 2055
},
{
  "type": "ITEM_UNDO",
  "timestamp": 1716272,
  "participantId": 7,
  "afterId": 0,
  "beforeId": 1058
},
{
  "type": "ITEM_PURCHASED",
  "timestamp": 1718091,
  "participantId": 7,
  "itemId": 1026
}]

Desired output: 期望的输出:

[{
  "type": "ITEM_PURCHASED",
  "timestamp": 1710829,
  "participantId": 2,
  "itemId": 3089
},
{
  "type": "ITEM_PURCHASED",
  "timestamp": 1714435,
  "participantId": 9,
  "itemId": 1037
},
{
  "type": "ITEM_PURCHASED",
  "timestamp": 1718091,
  "participantId": 7,
  "itemId": 1026
}]

I would like to filter this array and remove all the purchased items that were "undone". 我想过滤这个数组,并删除所有“撤消”的购买项目。 A PURCHASE_ITEM object can be undone by adding an ITEM_UNDONE object after it with a higher timestamp, a matching participantId and beforeId==itemId. 可以通过在具有更高时间戳,匹配的participantId和beforeId == itemId之后添加ITEM_UNDONE对象来撤消PURCHASE_ITEM对象。

I tried the following approach: 我尝试了以下方法:

  1. Collect all the ITEM_UNDO objects 收集所有ITEM_UNDO对象
  2. Find all corresponding ITEM_PURCHASED objects 找到所有相应的ITEM_PURCHASED对象
  3. Subtract both from the original list 从原始列表中减去两者

Step 2 is giving me trouble. 第2步给了我麻烦。 I have the following code so far which does not work: 到目前为止我有以下代码不起作用:

jq '
map(select(.type=="ITEM_UNDO")) as $undos |
 [
    {
      undo: $undos[],
      before_purchases: map( select(.type=="ITEM_PURCHASED"
                                    and .itemId == $undos[].beforeId
                                    and .participantId == $undos[].participantId
                                    )

                           )
    }
  ] as $undo_with_purchased | $undo_with_purchased
'

I know why it doesn't work because in the line 我知道为什么它不起作用,因为在线

and .itemId == $undos[].beforeId
and .participantId == $undos[].participantId

$undos is expanded twice independently rather than using the same instance for every comparison and then a third time in $ undos独立扩展两次,而不是每次比较使用相同的实例,然后是第三次

undo: $undos[],

I can't seem to find a good way to force jq to iterate over $undos only once and use the same instance for all comparisons. 我似乎无法找到一种强制方法来强制jq仅迭代$ undos一次并使用相同的实例进行所有比较。 In general I'm having issues iterating over multiple arrays at the same time, performing operations. 一般来说,我在同时迭代多个阵列时遇到问题,执行操作。 This would be a no brainer in any procedural language but what's the best way to do this kind of stuff in jq? 这在任何程序语言中都没有用,但是在jq中做这种事情的最佳方法是什么?

Thanks for any suggestions! 谢谢你的任何建议!

First, let's define a filter that will tell if an item in the array has been "undone" by a subsequent (in the array and in time) item. 首先,让我们定义一个过滤器,它将告诉数组中的一个项是否已被后续(在数组和时间中)项“撤消”。 This is straightforward to do using any/2 : 这很简单,使用any/2

# input: the entire array
# output: true iff item n is "undone" by a subsequent item
def undone($n):
  . as $in
  | length as $length
  | .[$n] as $nth
  | if $nth.type != "ITEM_PURCHASED" then false
    else any( range($n+1; $length) | $in[.]; 
              .type == "ITEM_UNDO"
              and .participantId == $nth.participantId
              and .beforeId== $nth.itemId
              and .timestamp > $nth.timestamp)
    end;

Now the query is quite straightforward: 现在查询非常简单:

[ range(0;length) as $i
  | select( (.[$i].type == "ITEM_PURCHASED") and (undone($i) | not) )
  | .[$i] ]

Invocation: jq -f program.jq data.json 调用:jq -f program.jq data.json

Output: an array with the three items. 输出:包含三个项目的数组。

Style 样式

One can write: 人们可以写:

range($n+1; $length) | $in[.]

more compactly, and perhaps more idiomatically, as: 更紧凑,也许更具惯用性,如:

$in[range($n+1; $length)]

In fact, both $in and $length can be dispensed with altogether, so the snippet in question would become simply: 事实上, $in$length都可以完全免除,所以有问题的片段会变得简单:

.[range($n+1; length)]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM