[
{
"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将对象数组转换为预期的格式。
打开和关闭操作将始终进行顺序执行,但不一定要完整设置(例如,开门有日志,而没有日志关门(因为门是开着的)
我更喜欢使用映射器方法/部分功能的逐步操作。
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)
答案 0 :(得分: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/ramda@0.26.1"></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
< em>和 timestamp
字段?)但是我怀疑我也错过了一些可以实现更好实现的东西。我很想听听它们是什么。
请注意,我选择使用最后一个date
字段,而不是最初的字段。有时,在reduce
调用中更容易做到这一点,听起来似乎并不重要。