使用ramdajs将记录数组转换为摘要或透视

问题描述 投票:0回答:1
[
  {
    "door_id": 324,
    "action": "door open",
    "timestamp": "2018-03-30 10:34:44",
    "date": "2018-03-30"
  },
  {
    "door_id": 324,
    "action": "door close",
    "timestamp": "2018-03-30 10:39:44",
    "date": "2018-03-30"
  },
  {
    "door_id": 324,
    "action": "door open",
    "timestamp": "2018-03-30 10:59:44",
    "date": "2018-03-30"
  },
  {
    "door_id": 325,
    "action": "door open",
    "timestamp": "2018-03-31 14:59:44",
    "date": "2018-03-31"
  },
  {
    "door_id": 325,
    "action": "door close",
    "timestamp": "2018-03-31 15:00:44",
    "date": "2018-03-31"
  }
]

我正在尝试使用ramda.js将这个对象数组转换为预期的格式。

打开和关闭操作将始终按顺序进行,但不一定是完整的设置(例如,有一个开门日志,但没有关闭门的日志,因为门是打开的)

我更喜欢使用mapper方法/部分功能一步一步。

const expected = [
  {
    "door_id": 324,
    "date": "2018-03-30",
    "status" : "Open",
    "actions_set_count": 2,
    "actions": [
      {
        "open": "2018-03-30 10:34:44",
        "close": "2018-03-30 10:39:44",
        "duration": 300
      },
      {
        "open": "2018-03-30 10:59:44",
        "close": null,
        "duration": null
      }
    ]
  },
  {
    "door_id": 325,
    "date": "2018-03-31",
    "status" : "Closed",
    "actions_set_count": 1,
    "actions": [
      {
        "open": "2018-03-30 14:59:44",
        "close": "2018-03-30 15:00:44",
        "duration": 60
      }
    ]
  }
]

到目前为止我做了什么,但远未完成

const isOpen = R.propEq('action','door open')
const isClosed = R.propEq('action','door close')


R.pipe(
  R.groupBy(R.prop('date')),
  R.map(R.applySpec({
    "date": R.pipe(R.head(), R.prop('date')),
    "door_id": R.pipe(R.head(), R.prop('door_id')),
    "open" : R.filter(isOpen),
    "close" : R.filter(isClosed),
    "sets": R.zip(R.filter(isOpen),R.filter(isClosed))
  })),
)(logs)
ramda.js
1个回答
1
投票

在这样的转型中,当我想不出优雅的东西时,我会回到reduce。使用groupBy(以及必要时,sortBy)和values,我们可以将数据放在一个允许我们做的顺序,然后做一个简单 - 如果有点乏味 - 减少它。

const duration = (earlier, later) => 
  (new Date(later) - new Date(earlier)) / 1000

const transform = pipe(
  groupBy(prop('door_id')),
  map(sortBy(prop('timestamp'))), // Perhaps unnecessary, if data is already sorted
  values,
  map(reduce((
    {actions, actions_set_count}, 
    {door_id, action, timestamp, date}
  ) => ({
    door_id, 
    date, 
    ...(action == "door open" 
      ? {
          status: 'Open',
          actions_set_count: actions_set_count + 1,
          actions: actions.concat({
            open: timestamp, 
            close: null, 
            duration: null
          })
        }
      : {
          status: 'Closed',
          actions_set_count,
          actions: [
            ...init(actions), 
            {
              ...last(actions), 
              close: timestamp, 
              duration: duration(last(actions).open, timestamp)
            }
          ]
        }
    )
  }), {actions: [], actions_set_count: 0}))
)

const doors = [
  {door_id: 324, action: "door open",  timestamp: "2018-03-30 10:34:44", date: "2018-03-30"},
  {door_id: 324, action: "door close", timestamp: "2018-03-30 10:39:44", date: "2018-03-30"},
  {door_id: 324, action: "door open",  timestamp: "2018-03-30 10:59:44", date: "2018-03-30"},
  {door_id: 325, action: "door open",  timestamp: "2018-03-31 14:59:44", date: "2018-03-31"},
  {door_id: 325, action: "door close", timestamp: "2018-03-31 15:00:44", date: "2018-03-31"}
]

console.log(transform(doors))
<script src="https://bundle.run/[email protected]"></script><script>
const {pipe, groupBy, prop, map, sortBy, values, reduce, init, last} = ramda    </script>

还有其他方法可以解决这个问题。我的第一个想法是使用splitEvery(2)在开 - 关对中获取这些,然后生成动作。问题是我们仍然需要实际的原始数据来填补其余的(door_iddate等)所以我最终得到了reduce

显然这远非优雅。部分原因是潜在的转变不是特别优雅(为什么actions_set_count领域,这只是actions的长度?),也不是数据(为什么有datetimestamp领域?)但我怀疑我也是错过了可以实现更好实施的东西。我很想听听那些是什么。

请注意,我选择使用最终的date字段而不是最初的字段。有时在reduce调用中更容易做到,听起来好像这还不重要。

© www.soinside.com 2019 - 2024. All rights reserved.