Ramda风格思维:不变性和数组

1.第一步
2.结合功能
3.部分使用(咖喱)
4.声明式编程
5.典型符号
6.不变性与对象
7.不变性和数组
8.镜片
9.结论


这篇文章是一系列名为Ramda Style Thinking的功能编程文章的第七部分。


第六部分中,我们讨论了以功能和不变的方式使用JavaScript对象。


在本文中,我们将讨论数组的类似工作。


读取数组元素


第六部分中,我们了解了用于读取对象属性(例如proppickhas的各种Ramda函数。 Ramda拥有更多读取数组元素的方法。


数组的prop的等效值为nth ; pick的等效项是slicehas的等效项是contains 。 让我们看看它们。


 const numbers = [10, 20, 30, 40, 50, 60] nth(3, numbers) // => 40 (  ) nth(-2, numbers) // => 50 (     ) slice(2, 5, numbers) // => [30, 40, 50] (. ) contains(20, numbers) // => true 

Slice接受两个索引,并返回一个子数组,该子数组从第一个索引(从零开始)开始,包括直到第二个索引的所有元素,但不包括该索引的元素。


可以访问数组的第一个和最后一个元素是很常见的,因此Ramda提供了针对这些情况的简短函数headlast 。 它还提供了获取除第一个( tail ),除最后一个( init )以外的所有元素,前N个元素( take(N) )和最后N个元素( takeLast(N) )的功能。 让我们看看它们的实际作用。


 const numbers = [10, 20, 30, 40, 50, 60] head(numbers) // => 10 tail(numbers) // => [20, 30, 40, 50, 60] last(numbers) // => 60 init(numbers) // => [10, 20, 30, 40, 50] take(3, numbers) // => [10, 20, 30] takeLast(3, numbers) // => [40, 50, 60] 

添加,更新和删除数组元素


通过研究如何使用对象,我们了解了assocdissocomit函数用于添加,更新和删除属性的知识。


由于数组具有有序的数据结构,因此我们有几种方法与对象的assoc相同。 最常见的是insertupdate ,但是Ramda还为典型的将元素添加到数组的开头和结尾的情况提供了appendprepend方法。 insertappendprepend向数组添加新元素; update使用新值“替换”阵列中的特定元素。


正如您可以从功能库中期望的那样,所有这些函数都返回一个具有预期更改的新数组。 原始数组永远不变。


 const numbers = [10, 20, 30, 40, 50, 60] insert(3, 35, numbers) // => [10, 20, 30, 35, 40, 50, 60] append(70, numbers) // => [10, 20, 30, 40, 50, 60, 70] prepend(0, numbers) // => [0, 10, 20, 30, 40, 50, 60] update(1, 15, numbers) // => [10, 15, 30, 40, 50, 60] 

为了将两个对象组合成一个对象,我们先前了解了merge方法。 Ramda还提供了concat方法来对数组执行相同的操作。


 const numbers = [10, 20, 30, 40, 50, 60] concat(numbers, [70, 80, 90]) // => [10, 20, 30, 40, 50, 60, 70, 80, 90] 

请注意,第二个数组已加入第一个数组。 当与其他代码分开使用此方法时,这似乎合乎逻辑,但是,与merge ,如果在管道中使用此方法,则此逻辑可能无法完全达到我们的期望。 我发现编写帮助函数concatAfterconst concatAfter = flip(concat)很有用,可以在我的管道中使用它。


Ramda还提供了一些删除项目的选项。 remove按其索引删除项目, 而不按其值删除项目。 在典型情况下,当我们从数组的开头或结尾删除元素时,也有诸如dropdropLast之类的方法。


 const numbers = [10, 20, 30, 40, 50, 60] remove(2, 3, numbers) // => [10, 20, 60] without([30, 40, 50], numbers) // => [10, 20, 60] drop(3, numbers) // => [40, 50, 60] dropLast(3, numbers) // => [10, 20, 30] 

请注意, remove接受一个索引和一个数量,而slice接受两个索引。 如果您不知道这种不一致,可能会造成混淆。


元素转换


与对象一样,我们可能希望通过将函数应用于原始值来更新数组元素。


 const numbers = [10, 20, 30, 40, 50, 60] //      10 update(2, multiply(10, nth(2, numbers)), numbers) // => [10, 20, 300, 40, 50, 60] 

为了简化这种典型情况,Ramda提供了一种adjust方法,其作用类似于对象的evolve 。 但是与evolve不同, adjust仅适用于数组的一个元素。


 const numbers = [10, 20, 30, 40, 50, 60] //      10 adjust(multiply(10), 2, numbers) 

请注意,将它们与update进行比较时,要adjust的前两个参数采用相反的方式。 这可能是错误的根源,但是在考虑部分应用程序时才有意义。 您可能希望自己进行adjust(multiply(10)) ,然后决定使用它来更改数组的哪个索引。


结论


现在,我们有了用于以声明性和不可变样式处理数组和对象的工具。 这使我们能够构建由小的,功能性的构建块组成的程序,结合可以满足我们需要的功能,而所有这些都无需更改所有数据结构。


下一个


我们已经学习了如何读取,更新和转换对象和数组元素的属性。 Ramda提供了执行这些操作的其他基本工具,镜头。 下一篇镜头文章将向我们展示它们如何工作。

Source: https://habr.com/ru/post/zh-CN415025/


All Articles