首页 > 解决方案 > 在 RamdaJS 中使用带参数的管道过滤器函数

问题描述

假设我有这两个过滤器:

const getNamesStartingWith = (letter) => persons => {
  return persons.filter(person => person.name.startsWith(letter);  
}

const getPersonsStartingWithZipCode = (zipCode) => persons => {
  return persons.filter(person => person.address.zipCode.startsWith(zipCode);
}

我想做一个通用的管道过滤器。

const pipeFilter = (funcArr, args) => arr => {
...
}

是要运行的funcArr函数的数组。这args是一个双精度数组,其中索引是函数的参数。

因此,对于我的示例函数,它将是:

const pipeFilter = ([getNamesStartingWith, getPersonsStartingWithZipCode], [['J'], [4]]) => persons => {..}

的论据getNamesStartingWithJ。的论据getPersonsStartingWithZipCode4

如果我愿意“手动”,我会这样做:

export const doPipeFiltering = (funcArr: any[], args: any[]) => (arr) => {

  return funcArr.reduce((acc, func, index) => {

    let filterdArr;
    if (index === 0) {
      filterdArr = func(...args[index])(arr);
    } else {
      filterdArr = func(...args[index])(acc);
    }
    acc = filterdArr;
    return acc;
  }, []);
};

作品。但我想在 RamdaJs 中做这件事,这样我就可以在那里使用所有简洁的功能。

我没有找到如何在 Ramda 中为不同的过滤器函数应用参数。可能吗?

标签: ramda.js

解决方案


使用 Ramda 函数,你绝对可以让它变得更干净。这是一种方法:

const doPipeFiltering = curry ( (funcArr, args, arr) => 
  reduce ( (acc, func) => func (acc), arr, zipWith (apply, funcArr, args) ))

const getNamesStartingWith = (letter) => (persons) => {
  return persons.filter(person => person.name.startsWith(letter)) 
}

const getPersonsStartingWithZipCode = (zipCode) => persons => {
  return persons.filter(person => person.address.zipCode.startsWith(zipCode))
}
                      
const people = [
  {name: 'Amanda', address: {zipCode: '86753'}}, 
  {name: 'Aaron', address: {zipCode: '09867'}},
  {name: 'Amelia', address: {zipCode: '85309'}}, 
  {name: 'Bob', address: {zipCode: '67530'}}
]

console .log (
  doPipeFiltering (
    [getNamesStartingWith, getPersonsStartingWithZipCode], 
    [['A'], ['8']],
    people
  )
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {curry, reduce, zipWith, apply} = R                    </script>

但我建议这不是一个非常符合人体工程学的 API。首先,如果过滤函数有一个完全通用的接口,它可能不会那么糟糕,但现在你必须提供两个单独的数组参数,它们的值必须同步。这对我来说是灾难的秘诀。

其次,我们必须为此创建过滤列表的函数。如果代码处理过滤,我会发现它更干净,而且我们只提供了一组简单的谓词。

所以这是一个替代建议,对于我认为更清洁的 API:

const runFilters = useWith (filter, [allPass, identity] )

// or one of these
// const runFilter = curry((filters, xs) => filter(allPass(filters), xs))
// const runFilters = curry ((filters, xs) => reduce ((a, f) => filter (f, a), xs, filters))

const nameStartsWith = (letter) => (person) => person.name.startsWith (letter)
const zipStartsWith = (digits) => (person) => person.address.zipCode.startsWith (digits)

const myFilter = runFilters ([nameStartsWith ('A'), zipStartsWith ('8')])

const people = [
  {name: 'Amanda', address: {zipCode: '86753'}}, 
  {name: 'Aaron', address: {zipCode: '09867'}},
  {name: 'Amelia', address: {zipCode: '85309'}}, 
  {name: 'Bob', address: {zipCode: '67530'}}
]

console .log (
  myFilter (people)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {useWith, filter, allPass, identity} = R               </script>

请注意,这里的 main 函数更简单,过滤谓词更简单,调用更明确。特别改进的是nameStartsWith ('A')zipStartsWith ('8')


推荐阅读