0

希望这是一个简单的好方法,但我就是不知道该怎么做。

我想用 rego 将数组中的项目映射到更干净的版本。例如从下面的数据

data = [
  {
    "some": "value",
    "another": "mvalue",
    "dont": "want"
  },
  {
    "some": "value1",
    "another": "mvalue1",
    "dont": "want1"
  },
  {
    "some": "value2",
    "another": "mvalue2",
    "dont": "want2"
  }
]

我想把数据变成

result = [
  {
    "some": "value",
    "another": "mvalue"
  },
  {
    "some": "value1",
    "another": "mvalue1"
  },
  {
    "some": "value2",
    "another": "mvalue2"
  }
]

我认为我最接近的两个是

result1 = cleaned {
    cleaned := {d | 
        d := {
            "some": data[_].some,
            "another": data[_].another
        }
    }
}

result2 = cleaned {
    d := data[_]
    cleaned := {
        "some": p.some,
        "another": p.another
    }
}
4

2 回答 2

1

在理解过程中拒绝键名怎么样?可能是一种更优雅的方式来做到这一点,但可能会有所帮助。

package play

reject(key) = result {
    remove := [ "dont", "someotherthing" ]
    result :=  key == remove[_]
}

result = d {
    d := [ obj | 
           val := input.data[_]; 
           obj := { k: v | 
                    v := val[k] 
                    not reject(k)
                  }
          ] 
 }

https://play.openpolicyagent.org/p/1A3DNLiNfj

于 2020-02-18T16:49:02.947 回答
1

TLDR;如果字段是静态的并且您可以轻松地枚举它们,那么您的两个解决方案几乎都是正确的(请参阅下文以了解它们为什么不正确。)这是正确的方法:

result = [
  mapped |
    original := data[_]
    mapped := {"some": original["some"], "another": original.another}
]

一个更优雅的选择是定义要包含或排除的字段,就像在@eephillip 的示例中一样。例如:

result = [
  mapped |
    original := data[_]
    mapped := {k: v |
      some k
      v := original[k]
      not exclude[k]}
]

exclude = {"dont", "dont2"}  # defines a SET of keys to exclude

当然,您可以通过使内部理解调用实现其他过滤器的函数来进一步概括它。

这是一个交互式示例:https ://play.openpolicyagent.org/p/P6wPd3rudJ


关于原始解决方案的两个注释。

1.result1没有data正确迭代

{d | 
  d := {
    "some": data[_].some,        # problem: _ is a different variable in each expression
    "another": data[_].another
  }
}

从概念上讲,每次出现_都是唯一的变量。如果显式声明变量,问题就更明显了:

# note: this is still wrong
{d | 
  some i, j
  d := {
    "some": data[i]["some"],
    "another": data[j].another
  }
}

如果你运行它,你会发现它产生了一个交叉产品(这不是你想要的)。您希望从同一个对象中选择“一些”和“另一个”字段,如下所示:

{d | 
  some i
  d := {
    "some": data[i]["some"],
    "another": data[i].another
  }
}

当然,想出唯一的变量名可能会很痛苦,所以你可以使用_. 只是不要将多个_变量误认为是指相同的值。我们可以重写语句来使用_如下:

{d | 
  obj := data[_]
  d := {
    "some": obj["some"],
    "another": obj.another
  }
}

result2接近但可以分配多个值(应该避免)

result2 = cleaned {
    d := data[_]
    cleaned := {               # problem: there could be multiple values for 'cleaned'
        "some": d["some"],
        "another": d.another
    }
}

如果满足中的陈述,则NAME = VALUE { BODY }分配VALUE给形式的规则。如果您省略,即您编写,则默认为(始终满足。)NAMEBODYBODYNAME = VALUEBODYtrue

在你上面的例子中:

  • NAMEresult2
  • VALUEcleaned
  • BODYd := data[_]; cleaned := {...}

在 Rego 中,我们将这些规则称为“完整规则”。完整的规则只是将单个值分配给变量的 IF-THEN 语句。“IF”部分是规则主体,“THEN”部分是分配。您应该避免编写可能将多个值分配给同一变量的规则,因为这可能会导致评估时间错误。例如:

# do not do this
result = v { 
   v := data[_]   # if 'data' is [1,2,3] then what is the value of 'result'? Hint: There is more than one answer.
}

如果要为变量分配多个值,则可以定义“部分规则”例如:

result[v] {       # result is a SET.
   v := data[_]
}
于 2020-02-19T17:05:58.303 回答