使用Ramda检测序列中的间隙?

时间:2017-10-04 04:05:21

标签: javascript functional-programming ramda.js

在ramda.js中,如何检测序列中是否存在大于某个值n的间隙?

例如,对于已经排序的值:

[
    {"name":"bob", loginDate:"2017-10-01"},
    {"name":"bob", loginDate:"2017-10-02"},
    {"name":"bob", loginDate:"2017-10-03"},
    {"name":"bob", loginDate:"2017-10-10"},
]

我如何检测任何两条记录的loginDates之间是否存在大于7的间隙?但不是在当前和第一个记录之间 - 只检查前一个记录。我不确定如何将当前项目与ramda中的上一项目进行比较。

2 个答案:

答案 0 :(得分:2)

当想要处理列表中的相邻值时,使用R.aperture创建可以像值的滑动窗口一样遍历的列表通常很有用。

执行列表的先前项和当前项之间的比较的二进制函数可以包含在R.apply中,以便它接受要比较的两个元素的数组。

用你的例子说明:

const maxGap = 1000 * 60 * 60 * 24 * 7 // 7 days in ms
const gtMaxGap = (prev, curr) =>
  maxGap + Date.parse(prev.loginDate) < Date.parse(curr.loginDate)

const anyGapTooLarge = R.pipe(R.aperture(2), R.any(R.apply(gtMaxGap)))

anyGapTooLarge([
  {"name":"bob", loginDate:"2017-10-01"},
  {"name":"bob", loginDate:"2017-10-02"},
  {"name":"bob", loginDate:"2017-10-03"},
  {"name":"bob", loginDate:"2017-10-10"},
]) // false

R.aperture的另一种替代方法是使用R.zip生成相邻值列表,以便用自己的尾部压缩列表。

答案 1 :(得分:1)

作为函数式编程的学生,我解决了这个问题。

我非常喜欢@Scott Christopher的回答;然而,在查看他所做的事情之前,我会管理出不同的解决方案。

该方法如下:

我以不同的方式完成了这项工作。如果您希望在给定时间段之后获得所有登录的列表并记下时间差异,则可以使用reduce然后使用filter来获取差距

首先,假设数据包含在名为data

的变量中

示例

const logs = [
  {"name":"bob", loginDate:"2017-10-01"},
  {"name":"bob", loginDate:"2017-10-02"},
  {"name":"bob", loginDate:"2017-10-03"},
  {"name":"bob", loginDate:"2017-10-10"},
]

接下来,我们创建一些实用程序函数和迭代器函数

const dateDifference = nextDate => initDate => {
  return ( new Date( nextDate ) - new Date( initDate ) )
}
const daysToMs = R.multiply(1000 * 60 * 60 * 24)

const differenceWithPreviousRecordIterator = dateField => differenceField => (acc,val) => {
  const timeDifference = acc.length  
    ? dateDifference
        (val[dateField])
        (R.last(acc)[dateField])
    : 0

  return R.append(
    R.assoc( differenceField, timeDifference, val ),
    acc
  )
}

现在配置迭代器函数

const iteratorFunc = differenceWithPreviousRecordIterator('loginDate')('difference')

然后获取所需时间后发生登录的行的行和过滤器

const rowsWithDifference = R.reduce(iteratorFunc,[])(logs)
const sevenDaysAndOlder = R.where({
  difference: R.gte( R.__, daysToMs(7) )
})
const filterForLoginsSevenDaysOrGreater = R.filter( sevenDaysAndOlder )

filterForLoginsSevenDaysOrGreater( rowsWithDifference )
// [ { name: 'bob', loginDate: '2017-10-10', difference: 604800000 } ]

那就是说,在看了@Scott的方法之后,通过略微修改他的解决方案可以实现类似的效果。修改即我们正在寻找等于或大于间隙的时间而不是大于。另一种方法是简单地使用filter代替any

const maxGap = 1000 * 60 * 60 * 24 * 7 // 7 days in ms
const gtMaxGap = (prev, curr) =>
  maxGap + Date.parse(prev.loginDate) <= Date.parse(curr.loginDate)

const anyGapTooLarge = R.pipe(R.aperture(2), R.filter( R.apply( gtMaxGap ) ))

anyGapTooLarge([
  {"name":"bob", loginDate:"2017-10-01"},
  {"name":"bob", loginDate:"2017-10-02"},
  {"name":"bob", loginDate:"2017-10-03"},
  {"name":"bob", loginDate:"2017-10-10"},
]) // gives pair where login was greater than or equal to maxGap