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

Detect gaps in a sequence using Ramda?

在 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"},
]

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

当想要处理列表中的相邻值时,创建一个可以像滑动 window 值一样遍历的列表通常很有用 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 用自己的尾部压缩列表来生成相邻值的列表。

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

我非常喜欢@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