基于属性值的对象的Ramda格式数组

时间:2018-10-31 13:29:28

标签: javascript ramda.js

如何将对象数组转换为格式化的对象数组,如下所示。

var a = 
[{"id": "001","project": "one","projectstartDate": "10/12/2018"},
{"id": "001","project": "one","projectstartDate": "10/15/2018"},
{"id": "001","project": "two","projectstartDate": "10/14/2018"},
{"id": "002","project": "one","projectstartDate": "10/12/2018"},
{"id": "002","project": "two","projectstartDate": "10/14/2018"}]

需要帮助使用ID然后按项目属性值分组和压缩

[
    {
        "name" : "001",
        "data" : [{
                "name" : "one",
                "data" : [ 
                    {"projectstartDate": "10/12/2018"},
                    {"projectstartDate": "10/15/2018"}
                ]
            },
            {
                "name" : "two",
                "data" : [ {"projectstartDate": "10/14/2018"}]
            }
        ] 
    },
    {
        "name" : "002",
        "data" : [
            {
                "name" : "one",
                "data" : [ {"projectstartDate": "10/12/2018"}]
            },
            {
                "name" : "two",
                "data" : [ {"projectstartDate": "10/14/2018"}]
            }
        ]
    }
]

尝试

R.pipe(R.groupBy(R.prop('id')),R.map(R.groupBy(R.prop('project'))))(a)

根据分组,但我需要命名道具和数据道具。

预先感谢

1 个答案:

答案 0 :(得分:0)

虽然groupBy可能是其中的主力军,但您的转型还有很多事情要做。 groupBy可以简单地创建一个对象,该对象具有传递给它的函数生成的键以及由生成该特定键的所有元素的数组组成的值。它很有用,但通常必须与其他东西结合使用。对于您的情况,您希望将其转换为具有namedata属性的对象,并且在这些数据对象中,要删除该键字段。

此外,您想执行两次:一次在id属性的初始数组上,然后一次在其data属性的新project数组上。因此,我们应该创建一个可重用的函数来为我们做到这一点,在初始数据上调用它,然后在第一个生成的data属性上调用它。

以下是Ramda解决方案:

const {pipe, groupBy, prop, toPairs, map, over, lensIndex, dissoc, zipObj, lensProp} = R

const groupInto = (name) => pipe(
  groupBy(prop(name)),
  toPairs,
  map(over(lensIndex(1), map(dissoc(name)))),
  map(zipObj(['name', 'data']))
)

const reformat = pipe(
  groupInto('id'),
  map(over(lensProp('data'), groupInto('project')))
)

const a = [
  {id: "001", project: "one", projectstartDate: "10/12/2018"},
  {id: "001", project: "one", projectstartDate: "10/15/2018"},
  {id: "001", project: "two", projectstartDate: "10/14/2018"},
  {id: "002", project: "one", projectstartDate: "10/12/2018"},
  {id: "002", project: "two", projectstartDate: "10/14/2018"}
]

console.log(reformat(a))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

dissoc(name)返回对象的近似克隆,仅缺少name属性。 over(lensIndex(1), fn)将索引1上的对象替换为对该对象调用fn的结果。同样,over(lensProp('data', fn)将属性data的值替换为对该属性调用fn的结果。 (在这两种情况下,“替换”实际上都是在重克隆近克隆。Ramda不会对用户数据进行变异。)zipObj最好用一个示例来描述:zipObj(['a', 'b'], [1, 2]) //=> {a: 1, b: 2}

在我看来,这是Ramda发光的领域之一。 (免责声明:我是Ramda的作者。)一旦您知道这些功能本身是做什么的,这便是关于如何解决问题的非常易读的描述,几乎没有多余的内容。

请注意groupInto本身的作用:

groupInto('id')(a) //=> [
//  {
//    name: '001'
//    data: [
//      {project: "one", projectstartDate: "10/12/2018"},
//      {project: "one", projectstartDate: "10/15/2018"},
//      {project: "two", projectstartDate: "10/14/2018"}
//    ],
//  },
//  {
//    name: "002"
//    data: [
//      {project: "one", projectstartDate: "10/12/2018"},
//      {project: "two", projectstartDate: "10/14/2018"}
//    ],
//  }
//]

在结果的每个data属性上针对“项目”再次运行它,将给出最终答案。

更新

以这种方式构建它,并使用Ramda来解决问题之后,现在我很容易在没有Ramda的情况下编写它,并且代码也没有我期望的那么糟糕:

const groupInto = (name) => (a) => Object.entries(a.reduce(
  (acc, {[name]: id, ...rest}) => ((acc[id] || (acc[id] = [])).push(rest), acc), 
  {}
)).map(([a, b]) => ({name: a, data: b}))

const format = (a) => groupInto('id')(a).map(
  ({name, data}) => ({name, data: groupInto('project')(data)})
)

var a = [
  {id: "001", project: "one", projectstartDate: "10/12/2018"},
  {id: "001", project: "one", projectstartDate: "10/15/2018"},
  {id: "001", project: "two", projectstartDate: "10/14/2018"},
  {id: "002", project: "one", projectstartDate: "10/12/2018"},
  {id: "002", project: "two", projectstartDate: "10/14/2018"}
]

console.log(format(a))

与上面的分解相同,并且过程几乎相同。对我来说,代码的可读性大大降低。但这也不可怕。如果目标环境中不包含Object.entries,则可能需要重构一下。那应该不太难。

但是直到我使用Ramda的工具构建第一种方法后,第二种方法才易于编写。 Ramda的pipe确实可以帮助我思考问题。