简体   繁体   English

创建数字数组的功能方法

[英]Functional way to create an array of numbers

How could I write the following code more functionally using ES6, without any 3rd party libraries? 如何使用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
}

I would like to avoid Array.push, and possibly the for loop, but I'm not sure how I would achieve it in this situation. 我想避免使用Array.push,可能还有for循环,但我不确定在这种情况下如何实现它。

  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);

This is a purely functional approach. 这是一种纯粹的功能性方法。 It uses Math.max/min to achieve the boundaries and then uses an IIFE to pass these boundaries to Array.from which will create an array of end - start elements and every of these elements will be the position in the array increased by the startvalue. 它使用Math.max/min来实现边界,然后使用IIFE将这些边界传递给Array.from ,它将创建一个end - start元素数组,并且每个元素都将是由startvalue增加的数组中的位置。


PS: IMO your code is actually much more concise (except from that unneccessary ternary) and far more readable than mys, just saying... PS:IMO你的代码实际上更简洁(除了那个不必要的三元组)并且比mys更具可读性,只是说......

Functional programming isn't limited to reduce , filter , and map ; 函数式编程不仅限于reducefiltermap ; it's about functions. 这是关于功能的。 This means we don't have to rely on perverse knowledge like Array.from ({ length: x }) where an object with a length property can be treated like an array. 这意味着我们不必依赖像Array.from ({ length: x })这样的反常知识,其中具有length属性的对象可以被视为数组。 This kind of behavior is bewildering for beginners and mental overhead for anyone else. 对于初学者来说,这种行为是令人困惑的,而对于其他任何人来说都是精神上的开销。 It think you'll enjoy writing programs that encode your intentions more clearly. 它认为你会喜欢编写更清晰地编码你的意图的程序。

reduce starts with 1 or more values and reduces to (usually) a single value. reduce以1或更多值开始,并减少到(通常)单个值。 In this case, you actually want the reverse of a reduce (or fold ), here called unfold . 在这种情况下,您实际上想要reduce (或fold )的反向 ,这里称为unfold The difference is we start with a single value, and expand or unfold it into (usually) multiple values. 不同之处在于我们从单个值开始,并将其展开或展开为(通常)多个值。

We start with a simplified example, alphabet . 我们从一个简化的例子开始, alphabet We begin unfolding with an initial value of 97 , the char code for the letter a . 我们开始展开初始值为97 ,字母a的字符代码。 We stop unfolding when the char code exceeds 122 , the char code for the letter z . 当char代码超过122 (字母z的char代码)时,我们停止展开。

 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 ] 

Above, we use a single integer for our state, but other unfolds may require a more complex representation. 在上面,我们对我们的状态使用单个整数,但是其他展开可能需要更复杂的表示。 Below, we show the classic Fibonacci sequence by unfolding a compound initial state of [ n, a, b ] where n is a decrementing counter, and a and b are numbers used to compute the sequence's terms. 下面,我们通过展开[ n, a, b ]的复合初始状态来显示经典的Fibonacci序列,其中n是递减计数器, ab是用于计算序列项的数字。 This demonstrates unfold can be used with any seed state, even arrays or objects. 这展示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 ]

Now we have the confidence to write pagination . 现在我们有信心写pagination Again, our initial state is compound data [ page, count ] as we need to keep track of the page to add, and how many pages ( count ) we've already added. 同样,我们的初始状态是复合数据[ page, count ]因为我们需要跟踪要添加的page ,以及我们已添加的页数( count )。

Another advantage to this approach is that you can easily parameterize things like 10 or -5 or +1 and there's a sensible, semantic structure to place them in. 这种方法的另一个优点是,您可以轻松地参数化诸如10-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 ] 

Above, there are two conditions which result in a call to done () . 上面,有两个条件导致调用done () We can collapse these using || 我们可以使用||来折叠它们 and the code reads a little nicer 并且代码读得更好一些

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 ]
    )

There are many ways to create an array functionally but creating an array depending on some correlated items like a math series is mostly done by unfolding. 有很多方法可以在功能上创建一个数组,但根据一些相关的项目(如数学系列)创建一个数组主要是通过展开来完成的。 You may consider unfolding like the inverse of reducing. 您可以考虑像减少的反转一样展开。 Your case do not necessarily require unfolding but just for the sake of proper functional programming lets see how it can be done. 您的情况不一定需要展开,但只是为了正确的函数编程,让我们看看它是如何完成的。

JS do not have a native unfolding function but we may simply implement it. JS没有本机展开功能,但我们可能只是实现它。 First of all what does unfold function look like..? 首先, 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);
};

As seen it takes 4 arguments. 如图所示,需要4个参数。

  1. p : This is a callback function just like we have in reduce. p这是一个回调函数,就像我们在reduce中一样。 It gets invoked with the current seed element e to be processed before insertion, it's index i to be inserted and the currently available array a like p(e,i,a) . 它被调用,当前种子元素e在插入之前被处理,它是要插入的索引i和当前可用的数组ap(e,i,a) When it returns a true , the unfolding operation concludes and returns the created array. 当它返回true ,展开操作结束并返回创建的数组。
  2. f : Is the function that we will apply for each item to be constructed. f我们将为每个项目申请的功能是否构建。 It takes a single argument which is the current iterating value. 它需要一个参数,它是当前的迭代值。 You may consider iterating value like the index value but we have control over how to iterate it. 您可以考虑像索引值一样迭代值,但我们可以控制如何迭代它。
  3. t : Is the function that we will apply the iterating value and get the next iterating value. t我们将应用迭代值并获得下一个迭代值的函数。 For index like iterations this should be x => x+1 . 对于类似迭代的索引,这应该是x => x+1
  4. v : Is the glorious initial value. v光荣的初始值。

So far so good. 到现在为止还挺好。 How are we going to use unfold to achieve this job. 我们如何利用unfold来完成这项工作。 First of all lets find our initial value by a function which takes page as an argument. 首先,让我们通过一个以页面为参数的函数来查找初始值。

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

How about the p function which decides where to stop? 那个决定停在哪里的p函数怎么样?

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

We will increase pages one by one but if we have a value greater than lastpage we need to feed null values instead. 我们将逐页增加页面,但如果我们的值大于lastpage则需要提供空值。 So f may look like 所以f可能看起来像

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

and finally t is the function how we increase the iterating value. 最后t是我们如何增加迭代值的函数。 It's x => x + 1 . 它是x => x + 1

 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); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM