映射数组中的项目

Mapping items in an array

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

我想使用 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
    }
}

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

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

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

如果你 运行 这个,你会发现它产生了叉积(这不是你想要的)。您希望从 相同的 对象中选择 "some" 和 "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
    }
}

如果满足 BODY 中的语句,NAME = VALUE { BODY } 形式的规则将 VALUE 分配给 NAME。如果省略 BODY,即写 NAME = VALUE,则 BODY 默认为 true(始终满足。)

在你上面的例子中:

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

在 Rego 中,我们称这些规则为 "complete rules"。完整的规则只是将单个值分配给变量的 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.
}

如果你想为一个变量分配多个值,那么你可以定义一个 "partial rule" 例如:

result[v] {       # result is a SET.
   v := data[_]
}