创建数字数组的功能方法

Fra*_*ens 7 javascript functional-programming

如何使用ES6在没有任何第三方库的情况下更功能地编写以下代码?

// sample pager array
// * output up to 11 pages
// * the current page in the middle, if page > 5
// * don't include pager < 1 or pager > lastPage
// * Expected output using example:
//     [9,10,11,12,13,14,15,16,17,18,19]

const page = 14 // by example
const lastPage = 40 // by example
const pagerPages = page => {
  let newArray = []
  for (let i = page - 5; i <= page + 5; i++) {
    i >= 1 && i <= lastPage ? newArray.push(i) : null
  }
  return newArray
}
Run Code Online (Sandbox Code Playgroud)

我想避免使用Array.push,可能还有for循环,但我不确定在这种情况下如何实现它.

Jon*_*lms 6

  const pageRange = (lastPage, page) => ((start, end) => Array.from({length: end - start + 1}, (_,i) => i + start))(Math.max(1, page - 5), Math.min(lastPage, page + 5));
 const newArray = pageRange(40, 14);
Run Code Online (Sandbox Code Playgroud)

这是一种纯粹的功能性方法.它用于Math.max/min实现边界,然后使用IIFE传递这些边界,Array.from这将创建一个end - start元素数组,并且每个元素都将是由startvalue增加的数组中的位置.


PS:IMO你的代码实际上更简洁(除了那个不必要的三元组)并且比mys更具可读性,只是说......


Tha*_*you 6

函数式编程不限于reduce, filter, 和map; 它是关于功能的。这意味着我们不必依赖不正当的知识,例如可以将Array.from ({ length: x })具有length属性的对象视为数组。这种行为对于初学者来说是令人困惑的,对其他人来说是精神负担。它认为您会喜欢编写更清晰地编码您的意图的程序。

reduce从 1 个或多个值开始,然后减少到(通常)一个值。在这种情况下,您实际上想要a (或)的反转,这里称为。不同之处在于我们从单个值开始,然后其扩展或展开为(通常)多个值。reducefoldunfold

我们从一个简化的例子开始,alphabet. 我们从初始值 开始展开,即字母97的字符代码a。当字符代码超过122字母的字符代码时,我们停止展开z

const unfold = (f, initState) =>
  f ( (value, nextState) => [ value, ...unfold (f, nextState) ]
    , () => []
    , initState
    )

const alphabet = () =>
  unfold
    ( (next, done, char) =>
        char > 122
          ? done ()
          : next ( String.fromCharCode (char) // value to add to output
                 , char + 1                   // next state
                 )
    , 97 // initial state
    )
    
console.log (alphabet ())
// [ a, b, c, ..., x, y, z ]
Run Code Online (Sandbox Code Playgroud)

上面,我们为我们的状态使用单个整数,但其他展开可能需要更复杂的表示。下面,我们通过展开复合初始状态来展示经典的斐波那契数列,[ n, a, b ]其中n是递减计数器,ab是用于计算序列项的数字。这演示了unfold可以用于任何种子状态,甚至是数组或对象。

const fib = (n = 0) =>
  unfold
    ( (next, done, [ n, a, b ]) =>
        n < 0
          ? done ()
          : next ( a                   // value to add to output
                 , [ n - 1, b, a + b ] // next state
                 )
    , [ n, 0, 1 ] // initial state
    )

console.log (fib (20))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 ]
Run Code Online (Sandbox Code Playgroud)

现在我们有信心写了pagination。同样,我们的初始状态是复合数据,[ page, count ]因为我们需要跟踪page要添加的内容以及count我们已经添加了多少页 ( )。

这种方法的另一个优点是您可以轻松地参数化诸如10or 之类的东西-5+1并且有一个合理的语义结构可以放置它们。

const unfold = (f, initState) =>
  f ( (value, nextState) => [ value, ...unfold (f, nextState) ]
    , () => []
    , initState
    )
    
const pagination = (totalPages, currentPage = 1) =>
  unfold
    ( (next, done, [ page, count ]) =>
        page > totalPages
          ? done ()
          : count > 10
            ? done ()
            : next (page, [ page + 1, count + 1 ])
    , [ Math.max (1, currentPage - 5), 0 ]
    )

console.log (pagination (40, 1))
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]

console.log (pagination (40, 14))
// [ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]

console.log (pagination (40, 38))
// [ 33, 34, 35, 36, 37, 38, 39, 40 ]

console.log (pagination (40, 40))
// [ 35, 36, 37, 38, 39, 40 ]
Run Code Online (Sandbox Code Playgroud)

以上,有两个条件会导致调用done (). 我们可以使用折叠这些||,代码读起来更好一点

const pagination = (totalPages, currentPage = 1) =>
  unfold
    ( (next, done, [ page, count ]) =>
        page > totalPages || count > 10
          ? done ()
          : next (page, [ page + 1, count + 1 ])
    , [ Math.max (1, currentPage - 5), 0 ]
    )
Run Code Online (Sandbox Code Playgroud)