创建数字数组的功能方法

时间:2018-04-30 22:24:29

标签: 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
}

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

3 个答案:

答案 0 :(得分: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);

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

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

答案 1 :(得分:4)

功能编程不限于reducefiltermap;关于功能。这意味着我们不必依赖像Array.from ({ length: x })这样的反常知识,其中具有length属性的对象可以被视为数组。对于初学者来说,这种行为是令人困惑的,而对于其他任何人来说都是精神上的开销。它认为你会喜欢编写更清晰地编码你的意图的程序。

reduce以1个或多个值开头,并减少为(通常)单个值。在这种情况下,您实际上需要reduce(或fold)的反向,此处称为unfold。不同之处在于我们从单个值开始,并将展开扩展为(通常)多个值。

我们从一个简化的例子alphabet开始。我们开始展开初始值97,即字母a的字符代码。当char代码超过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 ]的字符代码。

&#13;
&#13;
[ n, a, b ]
&#13;
&#13;
&#13;

上面,我们对我们的状态使用单个整数,但其他展开可能需要更复杂的表示。下面,我们通过展开n的复合初始状态来展示经典的斐波那契序列,其中a是递减计数器,而bunfold是用于计算序列的数字& #39;条款。这表明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 ] 可以用于任何种子状态,甚至是数组或对象。

pagination

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

此方法的另一个优点是,您可以轻松地对-5+1const 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 ]之类的内容进行参数化,并且可以使用合理的语义结构将其置于其中。

&#13;
&#13;
done ()
&#13;
&#13;
&#13;

上面,有两个条件导致调用||。我们可以使用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 ] ) 来折叠它们,并且代码读得更好

[node-js-getting-started]$ heroku local web
[OKAY] Loaded ENV .env File as KEY=VALUE Format
18:47:54 web.1   |  Listening on 5000

答案 2 :(得分:1)

有很多方法可以在功能上创建一个数组,但是根据一些相关的项目(如数学系列)创建一个数组主要是通过展开来完成的。您可以考虑像减少的反转一样展开。你的情况不一定需要展开,但只是为了正确的函数式编程,让我们看看它是如何完成的。

JS没有本机展开功能,但我们可能只是实现它。首先,unfold函数是什么样的..?

Array.unfold = function(p,f,t,v){
  var res = [],
   runner = d =>  p(d,res.length,res) ? [] : (res.push(f(d)),runner(t(d)), res);
  return runner(v);
};

如所见,需要4个参数。

  1. p这是一个回调函数,就像我们在reduce中一样。使用当前种子元素e调用它,以便在插入之前处理它,它的索引i将被插入,而当前可用的数组a就像p(e,i,a)一样。当它返回true时,展开操作结束并返回创建的数组。
  2. f :我们将为要构建的每个项目应用的功能。它需要一个参数,它是当前的迭代值。您可以考虑像索引值一样迭代值,但我们可以控制如何迭代它。
  3. t我们将应用迭代值并获取下一个迭代值的函数。对于像迭代这样的索引,这应该是x => x+1
  4. v是光荣的初始值。
  5. 到目前为止一切顺利。我们如何使用unfold来完成这项工作。首先,让我们通过一个以页面为参数的函数来找到我们的初始值。

    var v = (pg => pg - 5 > 0 ? pg - 5 : 1)(page)
    

    p函数决定在哪里停止?

    var p = (_,i) => i > 10
    

    我们会逐页增加页面,但如果我们的值大于lastpage,我们需要提供空值。所以f可能看起来像

    var f = (lp => v => v > lp ? null : v)(lastpage)
    

    最后t是我们如何增加迭代值的函数。这是x => x + 1

    &#13;
    &#13;
    Array.unfold = function(p,f,t,v){
      var res = [],
       runner = d =>  p(d,res.length,res) ? [] : (res.push(f(d)),runner(t(d)), res);
      return runner(v);
    };
    
    var v = pg => pg - 5 > 0 ? pg - 5 : 1,
        p = (_,i) => i > 10,
        f = lp => v => v > lp ? null : v,
        t = x => x + 1,
        a = Array.unfold(p,f(40),t,v(14));
    
    console.log(a);
    &#13;
    &#13;
    &#13;