Ramda 教程展示了如何使用 Ramda 库,它为 JavaScript 中的高级函数式编程提供了工具。在本文中,我们交替使用术语列表和数组。
拉姆达
Ramda 是一个面向 JavaScript 程序员的实用函数库。该库专注于不变性和无副作用的函数。 Ramda 函数也会自动柯里化,这允许通过不提供最终参数来简单地从旧函数构建新函数。
在本文中,我们在 Node 应用程序中使用 Ramda。
设置 Ramda
首先,我们安装 Ramda。
$ npm init -y
我们启动一个新的 Node 应用程序。
$ npm i ramda
我们使用npm i ramda
命令安装Ramda。
Ramda 加减函数
add
函数将两个值相加,subtract
函数将两个值相减。
import * as R from 'ramda'; console.log(R.add(2, 5)); console.log(R.subtract(2, 5)); let res = R.add(R.add(2, 5), R.subtract(2, 10)); console.log(res);
该示例同时使用了add
和subtract
函数。
let res = R.add(R.add(2, 5), R.subtract(2, 10));
在这里我们结合了这些功能。
$ node add_sub.js 7 -3 -1
Ramda 翻转函数
flip
函数从提供的函数返回一个新函数,其中参数被反转。
import * as R from 'ramda'; let val = R.subtract(2, 10); console.log(val); let val2 = R.flip(R.subtract)(2, 10); console.log(val2);
该示例使用subtract
反转flip
函数的参数。
$ node flipfun.js -8 8
Ramda调用函数
call
函数在以逗号分隔的参数上调用提供的函数。
import * as R from 'ramda'; let res = R.call(R.add, 1, 2); console.log(res); console.log(R.call(R.repeat, 'x')(5)); R.call(console.log, [1, 2, 3]);
该示例使用call
函数。
let res = R.call(R.add, 1, 2);
我们调用add
函数来添加两个整数。
console.log(R.call(R.repeat, 'x')(5));
我们调用 repeat
函数来生成包含五个“x”字母的列表。
R.call(console.log, [1, 2, 3]);
最后,我们使用call
函数输出列表。
$ node calling.js 3 [ 'x', 'x', 'x', 'x', 'x' ] [ 1, 2, 3 ]
Ramda 应用函数
apply
函数在参数列表上调用提供的函数。
import * as R from 'ramda'; let nums = [3, 5, 7, 8, 2, 1]; let res = R.apply(Math.min, nums); console.log(res); let res2 = R.apply(Math.max, nums); console.log(res2);
该示例使用apply
函数计算最小值和最大值。
let res = R.apply(Math.min, nums);
我们在Math.min
列表上调用nums
函数。我们从值中得到最小值。
$ node applyfun.js 1 8
我们得到最小值和最大值。
Ramda 自动柯里化
柯里化是将一个需要多个参数的函数转换为另一个函数的过程,当提供较少的参数时,返回一个等待剩余参数的新函数。
import * as R from 'ramda'; let addOneToAll = R.map(R.add(1)); let res = addOneToAll([1,2,3]); console.log(res);
在示例中,我们创建了一个 addOneToAll
函数,它将列表中的每个元素递增 1。
$ node currying.js [ 2, 3, 4 ]
Ramda head, tail, init, last 函数
head
返回给定列表或字符串的第一个元素。 tail
返回给定列表或字符串中除第一个元素以外的所有元素。init
返回给定列表或字符串中除最后一个元素之外的所有元素。 last
返回给定列表或字符串的最后一个元素。
import * as R from 'ramda'; let nums = [2, 4, 6, 8, 10]; console.log(R.head(nums)); console.log(R.tail(nums)); console.log(R.init(nums)); console.log(R.last(nums));
该示例对值数组使用head
、tail
、init
和last
函数。
$ node head_tail.js 2 [ 4, 6, 8, 10 ] [ 2, 4, 6, 8 ] 10
Ramda 长度函数
length
函数返回列表中元素的数量。
import * as R from 'ramda'; let nums = [1, 2, 2, 2, 3, 3, 4, 5, 5, 5, 6, 7]; let n1 = R.length(nums); console.log(n1); let n2 = R.length(R.uniq(nums)); console.log(n2);
在示例中,我们计算列表中元素的数量和列表中唯一元素的数量。
$ node lengthfn.js 12 7
列表中有十二个元素,列表中有七个唯一元素。
Ramda 属性函数
如果对象存在,prop
函数返回对象的指定属性。
import * as R from 'ramda'; console.log(R.prop('name', { name: 'John', age: 25 })); console.log(R.prop('age', { name: 'John', age: 25 }));
使用prop
函数,我们获取name
和age
属性的值。
$ node propfun.js John 25
Ramda 采摘功能
pluck
函数通过从提供的列表中的所有对象中提取指定的属性来返回一个新列表。
import * as R from 'ramda'; const users = [ { name: 'John', age: 25 }, { name: 'Lenny', age: 51 }, { name: 'Andrew', age: 43 }, { name: 'Peter', age: 81 }, { name: 'Anna', age: 43 }, { name: 'Albert', age: 76 }, { name: 'Adam', age: 47 }, { name: 'Robert', age: 72 } ]; console.log(R.pluck('age', users)); console.log(R.pluck('name', users));
使用pluck
函数,我们获取name
和age
属性并形成两个新列表。
$ node plucking.js [ 25, 51, 43, 81, 43, 76, 47, 72 ] [ 'John', 'Lenny', 'Andrew', 'Peter', 'Anna', 'Albert', 'Adam', 'Robert' ]
在下面的示例中,我们将使用形成的列表。
import * as R from 'ramda'; const users = [ { name: 'John', age: 25 }, { name: 'Lenny', age: 51 }, { name: 'Andrew', age: 43 }, { name: 'Peter', age: 81 }, { name: 'Anna', age: 43 }, { name: 'Albert', age: 76 }, { name: 'Adam', age: 47 }, { name: 'Robert', age: 72 } ]; let maxAge = R.apply(Math.max, R.pluck('age', users)); // let maxAge = Math.max(... R.pluck('age', users)); console.log(`The oldest person is ${maxAge} years old.`);
在示例中,我们找出一个人的最大年龄。
let maxAge = R.apply(Math.max, R.pluck('age', users));
通过对年龄列表调用Math.max
函数,我们得到最年长的年龄。
// let maxAge = Math.max(... R.pluck('age', users));
另一种注释解决方案使用扩展运算符而不是 apply
函数。
$ node plucking2.js The oldest person is 81 years old.
Ramda 拆分列表
使用splitEvery
函数,我们可以将列表拆分为指定长度的块。
import * as R from 'ramda'; let nums = [1, 2, 3, 4, 5, 6]; console.log(R.splitEvery(2, nums)); console.log(R.splitEvery(3, nums));
在示例中,我们将数组拆分为 2 和 3 个元素的块。
$ node chunks.js [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ] [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
Ramda 包含函数
如果指定的值在列表中,contains
函数返回 true。
import * as R from 'ramda'; const users = [ { name: 'John', age: 25 }, { name: 'Lenny', age: 51 }, { name: 'Andrew', age: 43 }, { name: 'Peter', age: 81 }, { name: 'Anna', age: 43 }, { name: 'Albert', age: 76 }, { name: 'Adam', age: 47 }, { name: 'Robert', age: 72 } ]; let isJohn = R.contains('John', R.pluck('name', users)); if (isJohn) { console.log('There is John in the list'); }
在示例中,我们检查指定的用户是否在列表中。
let isJohn = R.contains('John', R.pluck('name', users));
首先,我们使用pluck
函数从name
属性形成一个列表。然后我们用contains
检查’John’是否在列表中。
$ node containsfun.js There is John in the list
Ramda 范围函数
range
函数返回一个从起始值(含)到结束值(不含)的数字列表。
import * as R from 'ramda'; console.log(R.range(1, 10)); let vals = R.range(2, 12); vals.forEach(x => console.log(x));
该示例显示了如何使用range
函数。
console.log(R.range(1, 10));
在这一行中,我们创建了一个包含 1..9 个整数的列表。我们将它们打印到控制台。
let vals = R.range(2, 12); vals.forEach(x => console.log(x));
这里我们生成一个包含 2..11 个值的列表。我们使用 forEach
函数遍历列表。
$ node rangefun.js [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] 2 3 4 5 6 7 8 9 10 11
Ramda 求和函数
sum
函数对列表的所有元素求和。
import * as R from 'ramda'; let nums = [2, 4, 6, 8, 10]; console.log(R.sum(nums)); console.log(R.sum(R.range(1, 11)));
该示例使用 sum
函数对整数值求和。
let nums = [2, 4, 6, 8, 10]; console.log(R.sum(nums));
这里我们对nums
数组的值求和。
console.log(R.sum(R.range(1, 11)));
在这一行中,我们对 range
函数生成的列表的值求和。
$ node summation.js 30 55
Ramda产品功能
product
函数将 alist 的所有元素相乘。
import * as R from 'ramda'; let nums = [2, 4, 6, 8, 10]; console.log(R.product(nums));
该示例计算整数列表的乘积。
$ node productfun.js 3840
Ramda 排序,反向函数
sort
函数返回列表的副本,根据比较函数排序。比较器函数一次接受两个值,如果第一个值较小则返回负数,如果第一个值较大则返回正数,如果它们相等则返回零。
reverse
函数返回一个新列表或字符串,其中元素或字符的顺序相反。
import * as R from 'ramda'; let nums = [3, 1, 4, 2, 8, 5, 6]; console.log('sorting:') // sort ascending console.log(R.sort((x, y) => x - y , nums)); // sort descending console.log(R.sort((x, y) => y - x , nums)); console.log('reversing:') // reversing console.log(R.reverse(nums)); console.log(R.reverse('forest'));
该示例按升序和降序对整数进行排序,并将整数和字符串进行反向排序。
$ node sort_reverse.js sorting: [ 1, 2, 3, 4, 5, 6, 8 ] [ 8, 6, 5, 4, 3, 2, 1 ] reversing: [ 6, 5, 8, 2, 4, 1, 3 ] tserof
我们还可以使用内置的R.lt
和R.gt
比较器。
import * as R from 'ramda'; let nums = [3, 1, 4, 2, 8, 5, 6]; console.log('sorting:') // sort ascending console.log(R.sort(R.comparator(R.lt), nums)); // sort descending console.log(R.sort(R.comparator(R.gt), nums));
该示例按升序和降序对整数进行排序。
Ramda 排序函数
sortBy
函数根据提供的函数对列表进行排序。
import * as R from 'ramda'; const users = [ { name: 'John', age: 25 }, { name: 'Lenny', age: 51 }, { name: 'Andrew', age: 43 }, { name: 'Peter', age: 81 }, { name: 'Anna', age: 43 }, { name: 'Albert', age: 76 }, { name: 'Adam', age: 47 }, { name: 'Robert', age: 72 } ]; console.log('Sorted by age:'); let sortedByAge = R.sortBy(R.prop('age'), users); console.log(sortedByAge); console.log('Sorted by name:'); let sortedByName = R.sortBy(R.prop('name'), users); console.log(sortedByName);
在示例中,我们按age
和name
属性按升序对用户列表进行排序。
$ node sorting_objects.js Sorted by age: [ { name: 'John', age: 25 }, { name: 'Andrew', age: 43 }, { name: 'Anna', age: 43 }, { name: 'Adam', age: 47 }, { name: 'Lenny', age: 51 }, { name: 'Robert', age: 72 }, { name: 'Albert', age: 76 }, { name: 'Peter', age: 81 } ] Sorted by name: [ { name: 'Adam', age: 47 }, { name: 'Albert', age: 76 }, { name: 'Andrew', age: 43 }, { name: 'Anna', age: 43 }, { name: 'John', age: 25 }, { name: 'Lenny', age: 51 }, { name: 'Peter', age: 81 }, { name: 'Robert', age: 72 } ]
Ramda 查找、findLast 函数
find
函数返回列表中与谓词匹配的第一个元素,如果没有匹配则返回未定义。 findLast
函数返回列表中与谓词匹配的最后一个元素,如果没有元素匹配则返回未定义。
import * as R from 'ramda'; const isPositive = x => x > 0; let values = [-1, 0, -4, 5, 6, -1, 9, -2] let val = R.find(isPositive, values); console.log(val); let val2 = R.findLast(isPositive, values); console.log(val2);
在示例中,我们找到第一个和最后一个正值。
const isPositive = x => x > 0;
isPositive
是一个谓词函数,它为大于零的值返回 true。
let val = R.find(isPositive, values);
使用find
,我们找到第一次出现的正数。
let val2 = R.findLast(isPositive, values);
使用findLast
,我们找到最后一次出现的正数。
$ node finding.js 5 9
第一个正值是 5,最后一个是 9。
在下面的示例中,我们对对象列表使用find
函数。
import * as R from 'ramda'; const users = [ { name: 'John', age: 25 }, { name: 'Lenny', age: 51 }, { name: 'Andrew', age: 43 }, { name: 'Peter', age: 81 }, { name: 'Anna', age: 43 }, { name: 'Albert', age: 76 }, { name: 'Adam', age: 47 }, { name: 'Robert', age: 72 }, { name: 'Robert', age: 26 }, ]; console.log(R.find(R.propEq('name', 'Robert'))(users)); console.log(R.find(R.propEq('age', 81))(users));
通过结合使用find
和propEq
函数,我们可以查找具有指定属性的用户。
console.log(R.find(R.propEq('name', 'Robert'))(users));
我们在这里寻找一个名叫罗伯特的人。有两个罗伯茨,返回第一个匹配项。
$ node finding2.js { name: 'Robert', age: 72 } { name: 'Peter', age: 81 }
Ramda地图函数
map
函数将提供的函数映射到容器的每个值。
import * as R from 'ramda'; nums = [2, 4, 5, 6, 7, 8, 9]; let res = R.map(x => x * 2, nums); console.log(res); const isEven = x => x % 2 === 0; let res2 = R.map(isEven, nums); console.log(res2); let repeated = R.map(R.call, R.repeat(Math.random, 5)); console.log(repeated);
该示例演示了map
的用法。
let res = R.map(x => x * 2, nums);
我们在整数列表上映射一个匿名函数。生成一个新列表,其中每个值都乘以 2。
const isEven = x => x % 2 === 0; let res2 = R.map(isEven, nums);
这里我们对每个元素应用isEven
函数。 res2
是真值和假值的列表。如果我们只想选择事件编号,那么我们将使用 filter
函数。
let repeated = R.map(R.call, R.repeat(Math.random, 5));
在第三种情况下,我们生成一个包含五个随机值的列表。
$ node mapping.js [ 4, 8, 10, 12, 14, 16, 18 ] [ true, true, false, true, false, true, false ] [ 0.22019193556521865, 0.415950206671615, 0.8770997167119405, 0.23393806619678315, 0.8181008680173825 ]
Ramda过滤函数
filter
函数根据提供的谓词函数过滤可过滤对象(例如列表或普通对象)。 (谓词是返回布尔值的函数)。
import * as R from 'ramda'; nums = [-3, -1, 0, 2, 3, 4, 5, 6, 7] let res = R.filter(x => x > 0, nums); console.log(res); let res2 = R.filter(x => x < 0, nums); console.log(res2); const isEven = x => x % 2 === 0; let filtered = R.filter(isEven, nums); console.log(filtered);
在示例中,我们有一个整数值列表。我们使用filter
函数过滤掉正值、负值和偶数。
let res = R.filter(x => x > 0, nums);
此行中的filter
函数采用一个匿名函数,该函数对所有大于零的值返回真。然后将谓词应用于列表的每个元素。这样我们就形成了一个只包含正值的新列表。
$ node filtering.js [ 2, 3, 4, 5, 6, 7 ] [ -3, -1 ] [ 0, 2, 4, 6 ]
在下面的示例中,我们对用户列表应用filter
函数。
import * as R from 'ramda'; // senior is a person who is 70+ const users = [ { name: 'John', age: 25 }, { name: 'Lenny', age: 51 }, { name: 'Andrew', age: 43 }, { name: 'Peter', age: 81 }, { name: 'Anna', age: 43 }, { name: 'Albert', age: 76 }, { name: 'Adam', age: 47 }, { name: 'Robert', age: 72 } ]; console.log(R.filter(user => user.age >= 70, users));
该示例过滤掉高级用户。我们将老年人定义为 70 岁及以上的人。
$ node filtering2.js [ { name: 'Peter', age: 81 }, { name: 'Albert', age: 76 }, { name: 'Robert', age: 72 } ]
我们有三个高级用户。
拒绝函数
reject
是对filter
的补充。它排除谓词返回 true 的可过滤元素。
import * as R from 'ramda'; const users = [ { name: 'John', city: 'London', born: '2001-04-01' }, { name: 'Lenny', city: 'New York', born: '1997-12-11' }, { name: 'Andrew', city: 'Boston', born: '1987-02-22' }, { name: 'Peter', city: 'Prague', born: '1936-03-24' }, { name: 'Anna', city: 'Bratislava', born: '1973-11-12' }, { name: 'Albert', city: 'Bratislava', born: '1940-18-19' }, { name: 'Adam', city: 'Trnava', born:'1983-12-01' }, { name: 'Robert', city: 'Bratislava', born: '1935-05-15' }, { name: 'Robert', city: 'Prague', born:'1998-03-14' } ]; let res = R.reject(R.propEq('city', 'Bratislava'))(users); console.log(res); let res2 = R.filter(R.propEq('city', 'Bratislava'))(users); console.log(res2);
在示例中,我们使用reject
函数形成一个新的不包含布拉迪斯拉发市的对象列表。我们还使用filter
函数来形成包含布拉迪斯拉发市的新对象列表。
$ node rejecting.js [ { name: 'John', city: 'London', born: '2001-04-01' }, { name: 'Lenny', city: 'New York', born: '1997-12-11' }, { name: 'Andrew', city: 'Boston', born: '1987-02-22' }, { name: 'Peter', city: 'Prague', born: '1936-03-24' }, { name: 'Adam', city: 'Trnava', born: '1983-12-01' }, { name: 'Robert', city: 'Prague', born: '1998-03-14' } ] [ { name: 'Anna', city: 'Bratislava', born: '1973-11-12' }, { name: 'Albert', city: 'Bratislava', born: '1940-18-19' }, { name: 'Robert', city: 'Bratislava', born: '1935-05-15' } ]
这是输出。第一个列表包含所有不包含 Bratislava city 属性的对象。第二个只包含具有布拉迪斯拉发城市属性的对象。
分区函数
partition
函数将 filterable 分为两个独立的对象:一个满足谓词,一个不满足谓词。
import * as R from 'ramda'; let nums = [4, -5, 3, 2, -1, 7, -6, 8, 9]; let [ neg, pos ] = R.partition(e => e < 0, nums); console.log(neg); console.log(pos);
使用 partition
函数,我们将整数列表分成两个单独的列表:负数和正数。
$ node partitionfun.js [ -5, -1, -6 ] [ 4, 3, 2, 7, 8, 9 ]
第一个列表包含负值,第二个列表包含正值。
Ramda groupBy 函数
groupBy
函数根据对每个元素调用字符串返回函数的结果,将列表拆分为存储在对象中的子列表,并根据返回值对结果进行分组。
import * as R from 'ramda'; let students = [ { name: 'Adam', score: 84 }, { name: 'Eddy', score: 58 }, { name: 'Peter', score: 69 }, { name: 'Roman', score: 93 }, { name: 'Jane', score: 56 }, { name: 'Lucy', score: 76 }, { name: 'Jack', score: 88 }, ]; var groupByGrade = R.groupBy((student) => { let score = student.score; return score < 65 ? 'F' : score < 70 ? 'D' : score < 80 ? 'C' : score < 90 ? 'B' : 'A'; }); let grouped = groupByGrade(students); console.log('Student(s) having A grade:'); console.log(grouped['A']); console.log('Student(s) having B grade:'); console.log(grouped['B']); console.log('Student(s) having C grade:'); console.log(grouped['D']); console.log('Student(s) having D grade:'); console.log(grouped['D']); console.log('Student(s) having F grade:'); console.log(grouped['F']);
在示例中,我们按学生的分数将学生分组到年级子列表中。
$ node grouping.js Student(s) having A grade: [ { name: 'Roman', score: 93 } ] Student(s) having B grade: [ { name: 'Adam', score: 84 }, { name: 'Jack', score: 88 } ] Student(s) having C grade: [ { name: 'Peter', score: 69 } ] Student(s) having D grade: [ { name: 'Peter', score: 69 } ] Student(s) having F grade: [ { name: 'Eddy', score: 58 }, { name: 'Jane', score: 56 } ]
Ramda 归约函数
reduce
函数将列表值聚合为单个值。它对累加器和列表中的每个元素(从左到右)应用一个函数,以将其减少为单个值。
import * as R from 'ramda'; let nums = [2, 3, 4, 5, 6, 7]; let sum = R.reduce((x, y) => x+y, 0, nums); console.log(sum); let product = R.reduce((x, y) => x*y, 1, nums); console.log(product);
该示例使用reduce
函数计算整数列表的总和和乘积。
let sum = R.reduce((x, y) => x+y, 0, nums);
在这一行中,我们计算了值的总和。第一个参数是应用于值的函数。第二个是累加器,也就是起始值。第三个是包含值的列表。
let product = R.reduce((x, y) => x*y, 1, nums);
这里我们计算列表值的乘积。
$ node reducefun.js 27 5040
以下示例计算表达式:1*2 + 3*4 + 5*6
。
import * as R from 'ramda'; let nums = [1, 2, 3, 4, 5, 6]; let ret = R.reduce((acc, x) => acc + x[0] * x[1], 0, R.splitEvery(2, nums)); console.log(ret);
在示例中,我们将列表分成对,并对这些对应用缩减操作。
$ node reduce_fun2.js 44
Ramda where 函数
where
函数允许对对象创建复杂的查询。
import * as R from 'ramda'; const moment = require('moment'); const users = [ { name: 'John', city: 'London', born: '2001-04-01' }, { name: 'Lenny', city: 'New York', born: '1997-12-11' }, { name: 'Andrew', city: 'Boston', born: '1987-02-22' }, { name: 'Peter', city: 'Prague', born: '1936-03-24' }, { name: 'Anna', city: 'Bratislava', born: '1973-11-18' }, { name: 'Albert', city: 'Bratislava', born: '1940-12-11' }, { name: 'Adam', city: 'Trnava', born: '1983-12-01' }, { name: 'Robert', city: 'Bratislava', born: '1935-05-15' }, { name: 'Robert', city: 'Prague', born: '1998-03-14' } ]; let res1 = R.filter(R.where({ city: R.equals('Bratislava') }))(users); console.log(res1); let res2 = R.filter(R.where({ city: R.equals('Bratislava'), name: R.startsWith('A') }))(users); console.log(res2); let res3 = R.filter(R.where({ born: (dt) => getAge(dt) > 40}))(users); console.log(res3); function getAge(dt) { return moment.duration(moment() - moment(dt, 'YYYY-MM-DD', true)).years(); }
在示例中,我们在用户列表中使用where
创建查询。
let res1 = R.filter(R.where({ city: R.equals('Bratislava') }))(users);
在这里我们找出所有居住在布拉迪斯拉发的用户。
let res2 = R.filter(R.where({ city: R.equals('Bratislava'), name: R.startsWith('A') }))(users);
在此代码中,我们找出居住在布拉迪斯拉发且他们的名字以“A”开头的用户。
let res3 = R.filter(R.where({ born: (dt) => getAge(dt) > 40}))(users);
最后,我们找出 40 岁以上的用户。
function getAge(dt) { return moment.duration(moment() - moment(dt, 'YYYY-MM-DD', true)).years(); }
要根据提供的出生日期计算年龄,我们使用 moment 模块。
$ node where_fun.js [ { name: 'Anna', city: 'Bratislava', born: '1973-11-18' }, { name: 'Albert', city: 'Bratislava', born: '1940-12-11' }, { name: 'Robert', city: 'Bratislava', born: '1935-05-15' } ] [ { name: 'Anna', city: 'Bratislava', born: '1973-11-18' }, { name: 'Albert', city: 'Bratislava', born: '1940-12-11' } ] [ { name: 'Peter', city: 'Prague', born: '1936-03-24' }, { name: 'Anna', city: 'Bratislava', born: '1973-11-18' }, { name: 'Albert', city: 'Bratislava', born: '1940-12-11' }, { name: 'Robert', city: 'Bratislava', born: '1935-05-15' } ]
在本文中,我们使用了Ramda
库。
列出所有 JavaScript 教程。