[
{
"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)
在这样的转型中,当我想不出优雅的东西时,我会回到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_id
,date
等)所以我最终得到了reduce
。
显然这远非优雅。部分原因是潜在的转变不是特别优雅(为什么actions_set_count
领域,这只是actions
的长度?),也不是数据(为什么有date
和timestamp
领域?)但我怀疑我也是错过了可以实现更好实施的东西。我很想听听那些是什么。
请注意,我选择使用最终的date
字段而不是最初的字段。有时在reduce
调用中更容易做到,听起来好像这还不重要。