[英]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
; 函数式编程不仅限于
reduce
, filter
和map
; 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
是递减计数器, a
和b
是用于计算序列项的数字。 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个参数。
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
和当前可用的数组a
如p(e,i,a)
。 When it returns a true
, the unfolding operation concludes and returns the created array. true
,展开操作结束并返回创建的数组。 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. 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
。 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.