ramda整理
ramda柯里化实现(源码)
柯里化简单实现
一个参数柯里化实现function _curry1(fn) {return function f1(a) {if (arguments.length === 0) {return f1;} else {return fn.apply(this, arguments);}};}<br />const curryMathAbs = _curry1(Math.abs);curryMathAbs(-3) // 3curryMathAbs()(-3) // 3
二个参数柯里化实现function _curry2(fn) {return function f2(a, b) {switch (arguments.length) {case 0:return f2;case 1:return _curry1(function(_b) { return fn(a, _b); });default:return fn(a, b);}};}
三个参数柯里化实现function _curry3(fn) {return function f3(a, b, c) {switch (arguments.length) {case 0:return f3;<br />case 1:return _curry2(function (_b, _c) {return fn(a, _b, _c);});<br />case 2:return _curry1(function (_c) {return fn(a, b, _c);});<br />default:return fn(a, b, c);}};}
传入占位符的柯里化实现
想要的效果g = curry(f)g(1)(2)(3)<br />也可以g2 = g(_)(2)(3)g2(1)
方案// 示例R.add(R.__, 2)(3)<br />// R.__表示占位符,源码__.jsmodule.exports = {'@@functional/placeholder': true};<br />// 判断是否是占位符,源码_isPlaceholder.jsfunction _isPlaceholder(a) {return a != null && typeof a === 'object' && a['@@functional/placeholder'] === true;}
一个参数柯里化实现function _curry1(fn) {return function f1(a) {// 若执行函数,无参数或传入占位符,则返回原函数// curryMathAbs = _curry1(Math.abs);// curryMathAbs(-3) // 3// curryMathAbs()(-3) // 3if (arguments.length === 0 || _isPlaceholder(a)) {return f1;} else {return fn.apply(this, arguments);}};}
二个参数柯里化实现function _curry2(fn) {return function f2(a, b) {switch (arguments.length) {case 0:return f2;<br />case 1:// 若传入1个参数,且为占位符,则返回原函数,参照【一个参数柯里化实现】示例// 否则调用_curry1,且fn函数的参数为_b,为后续参数// R.add(7)(10);return _isPlaceholder(a) ? f2 : _curry1(function (_b) {return fn(a, _b);});<br />default:// 若全是占位符则返回原函数,参照【一个参数柯里化实现】示例// 若a传入占位符,调用_curry1,且fn函数的参数为_a,将后续参数当做a再次传入;同理b为占位符// R.add(7)(10);return _isPlaceholder(a) && _isPlaceholder(b) ? f2 : _isPlaceholder(a) ? _curry1(function (_a) {return fn(_a, b);}) : _isPlaceholder(b) ? _curry1(function (_b) {return fn(a, _b);}) : fn(a, b);}};}
三个参数柯里化实现function _curry3(fn) {return function f3(a, b, c) {switch (arguments.length) {case 0:return f3;<br />case 1:// 若传入1个参数,且为占位符,则返回原函数,参照【一个参数柯里化实现】示例// 否则调用_curry2,且fn函数的参数为_b, _c,为后续参数return _isPlaceholder(a) ? f3 : _curry2(function (_b, _c) {return fn(a, _b, _c);});<br />case 2:// 若全是占位符则返回原函数// 若a传入占位符,调用_curry2,且fn函数的参数为_a, _c,将后续参数作为相应位置数据传入;同理b传入占位符,调用_curry2,且fn函数的参数为_b, _c,将后续参数作为相应位置数据传入;否则调用_curry1,且fn函数的参数为_c, 为后续参数// R.add(7)(10);return _isPlaceholder(a) && _isPlaceholder(b) ? f3 : _isPlaceholder(a) ? _curry2(function (_a, _c) {return fn(_a, b, _c);}) : _isPlaceholder(b) ? _curry2(function (_b, _c) {return fn(a, _b, _c);}) : _curry1(function (_c) {return fn(a, b, _c);});<br />default:// 同理上述,排列组合return _isPlaceholder(a) && _isPlaceholder(b) && _isPlaceholder(c) ? f3 : _isPlaceholder(a) && _isPlaceholder(b) ? _curry2(function (_a, _b) {return fn(_a, _b, c);}) : _isPlaceholder(a) && _isPlaceholder(c) ? _curry2(function (_a, _c) {return fn(_a, b, _c);}) : _isPlaceholder(b) && _isPlaceholder(c) ? _curry2(function (_b, _c) {return fn(a, _b, _c);}) : _isPlaceholder(a) ? _curry1(function (_a) {return fn(_a, b, c);}) : _isPlaceholder(b) ? _curry1(function (_b) {return fn(a, _b, c);}) : _isPlaceholder(c) ? _curry1(function (_c) {return fn(a, b, _c);}) : fn(a, b, c);}};}
多个参数演进及实现
通常的柯里化的实现:function curry (fn) {return function f() {const args = [].slice.call(arguments);if(args.length < fn.length) {return f.apply(this, args.concat([].slice.call(arguments)))} else {return fn.apply(this, args);}}}<br />
缺点:
- 调用柯里化的函数后无法知道执行了部分参数的函数,还需要几个参数。
- 传入参数位置必须和函数接受的参数位置保持一致
优化多个参数过程:
最终结果(可以放在控制台自行运行)function _arity(n, fn) {/* eslint-disable no-unused-vars */switch (n) {case 0:return function () {return fn.apply(this, arguments);};<br />case 1:return function (a0) {return fn.apply(this, arguments);};<br />case 2:return function (a0, a1) {return fn.apply(this, arguments);};<br />case 3:return function (a0, a1, a2) {return fn.apply(this, arguments);};<br />case 4:return function (a0, a1, a2, a3) {return fn.apply(this, arguments);};<br />case 5:return function (a0, a1, a2, a3, a4) {return fn.apply(this, arguments);};<br />case 6:return function (a0, a1, a2, a3, a4, a5) {return fn.apply(this, arguments);};<br />case 7:return function (a0, a1, a2, a3, a4, a5, a6) {return fn.apply(this, arguments);};<br />case 8:return function (a0, a1, a2, a3, a4, a5, a6, a7) {return fn.apply(this, arguments);};<br />case 9:return function (a0, a1, a2, a3, a4, a5, a6, a7, a8) {return fn.apply(this, arguments);};<br />case 10:return function (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) {return fn.apply(this, arguments);};<br />default:throw new Error('First argument to _arity must be a non-negative integer no greater than ten');}}var _ = { '@@function/placeholder' : true};function _isPlaceholder(a) {return a != null && typeof a === 'object' && a['@@function/placeholder'] === true;}<br />function _curry1(fn) {return function f1(a) {if (arguments.length === 0 || _isPlaceholder(a)) {return f1;} else {return fn.apply(this, arguments);}};}<br />function _curry2(fn) {return function f2(a, b) {switch (arguments.length) {case 0:return f2;<br />case 1:return _isPlaceholder(a) ? f2 : _curry1(function (_b) {return fn(a, _b);});<br />default:return _isPlaceholder(a) && _isPlaceholder(b) ? f2 : _isPlaceholder(a) ? _curry1(function (_a) {return fn(_a, b);}) : _isPlaceholder(b) ? _curry1(function (_b) {return fn(a, _b);}) : fn(a, b);}};}<br />function _curryN(length, received, fn) {return function () {var combined = [];var argsIdx = 0;var left = length;var combinedIdx = 0;console.log('jinlai', combinedIdx, received, argsIdx, arguments)while (combinedIdx < received.length || argsIdx < arguments.length) {var result;console.log('循环', combinedIdx, received, !_isPlaceholder(received[combinedIdx]), argsIdx, arguments, combinedIdx < received.length && (!_isPlaceholder(received<br />[combinedIdx]) || argsIdx >= arguments.length))<br />if (combinedIdx < received.length && (!_isPlaceholder(received[combinedIdx]) || argsIdx >= arguments.length)) {result = received[combinedIdx];} else {result = arguments[argsIdx];argsIdx += 1;}<br />combined[combinedIdx] = result;<br />if (!_isPlaceholder(result)) {left -= 1;}<br />console.log('jiesu', left, combined)<br />combinedIdx += 1;}<br />return left <= 0 ? fn.apply(this, combined) : _arity(left, _curryN(length, combined, fn));};}<br />var curryN =_curry2(function curryN(length, fn) {if (length === 1) {return _curry1(fn);}<br />return _arity(length, _curryN(length, [], fn));});<br />function say(name, age, like) { console.log(我叫${name},我${age}岁了, 我喜欢${like}) };var msg = curryN(3, say)msg(_, 20)('大西瓜', _,) ('妹子') // 我叫大西瓜,我20岁了, 我喜欢妹子msg(_, _, '瞎bb')(_, '25')('小hb') // 我叫小hb,我25岁了, 我喜欢瞎bbmsg('小明')(_, _)(22, '小红') // 我叫小明,我22岁了, 我喜欢小红
step1: 解决【调用柯里化的函数后无法知道执行了部分参数的函数,还需要几个参数】// 源码_arity.js// 包裹一个函数,返回一个确定参数的函数。一般来说,函数的复杂度是和他自身的参数成正比的,函数接收的函数越多,那么函数的复杂度就越高,虽然JavaScript中没有明确规定的传入参数的个数(好像是225个?),但是这里限制如果一个函数的参数超过10个那么就抛出错误function _arity(n, fn) {/* eslint-disable no-unused-vars */switch (n) {case 0:return function () {return fn.apply(this, arguments);};<br />case 1:return function (a0) {return fn.apply(this, arguments);};<br />case 2:return function (a0, a1) {return fn.apply(this, arguments);};<br />case 3:return function (a0, a1, a2) {return fn.apply(this, arguments);};<br />case 4:return function (a0, a1, a2, a3) {return fn.apply(this, arguments);};<br />case 5:return function (a0, a1, a2, a3, a4) {return fn.apply(this, arguments);};<br />case 6:return function (a0, a1, a2, a3, a4, a5) {return fn.apply(this, arguments);};<br />case 7:return function (a0, a1, a2, a3, a4, a5, a6) {return fn.apply(this, arguments);};<br />case 8:return function (a0, a1, a2, a3, a4, a5, a6, a7) {return fn.apply(this, arguments);};<br />case 9:return function (a0, a1, a2, a3, a4, a5, a6, a7, a8) {return fn.apply(this, arguments);};<br />case 10:return function (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) {return fn.apply(this, arguments);};<br />default:throw new Error('First argument to _arity must be a non-negative integer no greater than ten');}}
step2: 多函数柯里化版本1(进阶过程的演变,源码与此不同)function curry (length, recived, fn) {return function() {var args = [].slice.call(arguments);var combined = recived.concat(args);`<br /> if(combined.length < length ) {<br /> return arity(length - combined.length, curry(length, combined, fn)); <br /> } else {<br /> return fn.apply(this, combined);<br /> }<br /> }<br /> }<br /><br /> const a = (x, y, z) => x+y+z;<br /> const b = curry(3, [], a)(1);<br /> console.log(b.length) //=> 2<br /> <br /> const c = curry(3, [], a)(1, 2);<br /> console.log(c.length) //=> 1<br /> <br /> const d = curry(3, [], a)(1)(2);<br /> console.log(d) //=> 1`
结论:以上可以获取剩余参数个数,解决问题一
step3: 解决【传入参数位置必须和函数接受的参数位置保持一致】function _curryN(length, received, fn) {return function () {// 存放每次调用函数参数的数组var combined = [];// 入参的循环索引var argsIdx = 0;// 入参个数var left = length;// 每次调用函数参数的数组的索引var combinedIdx = 0;`<br /> //这里同时迭代recived和arguments。 <br /> //我们要循环取出每一次curryN初始化接收到的参数和调用函数时传入的参数保存在combined中 <br /> // 重点难点:将参数进行过滤,保证入参位置统一<br /> while (combinedIdx < received.length || argsIdx < arguments.length) {<br /> var result;<br /> //首先迭代recived,取出不是占位符的参数放入combined中<br /> if (combinedIdx < received.length && (!_isPlaceholder(received[combinedIdx]) || argsIdx >= arguments.length)) {<br /> result = received[combinedIdx];<br /> } else {<br /> //如果recived中不是占位符的参数已经迭代完了,那么将arguments放入combined中<br /> result = arguments[argsIdx];<br /> argsIdx += 1;<br /> }<br /><br /> combined[combinedIdx] = result;<br /> //如果当前参数不是占位符,则长度减1<br /> if (!_isPlaceholder(result)) {<br /> left -= 1;<br /> }<br /><br /> combinedIdx += 1;<br /> }<br /> //如果传入参数满足fn参数个数,则直接调用fn,否则递归调用curry函数,反复过滤掉recived的占位符<br /> return left <= 0 ? fn.apply(this, combined) : _arity(left, _curryN(length, combined, fn));<br /> };<br />}`
柯里化使用场景
参数复用
- 场景1
//柯里化实际是把简答的问题复杂化了,但是复杂化的同时,我们在使用函数时拥有了更加多的自由度// 示例缺点:我们每次进行校验的时候都需要输入一串正则,再校验同一类型的数据时,相同的正则我们需要写多次, 这就导致我们在使用的时候效率低下,并且由于 checkByRegExp 函数本身是一个工具函数并没有任何意义, 一段时间后我们重新来看这些代码时,如果没有注释,我们必须通过检查正则的内容, 我们才能知道我们校验的是电话号码还是邮箱,还是别的什么function checkByRegExp(regExp,string) { return regExp.test(string); }// 校验电话号码checkByRegExp(/^1\d{10}$/, '18642838455');// 校验电话号码checkByRegExp(/^1\d{10}$/, '13109840560'); // 校验电话号码// 校验邮箱checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@163.com');// 校验邮箱checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@qq.com');
优化://进行柯里化,实现参数**复用**let _check = curry(checkByRegExp);//生成工具函数,验证电话号码let checkCellPhone = _check(/^1\d{10}$/);//生成工具函数,验证邮箱let checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/);<br />checkCellPhone('18642838455'); // 校验电话号码checkCellPhone('13109840560'); // 校验电话号码<br />checkEmail('test@163.com'); // 校验邮箱checkEmail('test@qq.com'); // 校验邮箱
- 场景2
//需要获取数据中的所有 name 属性的值,常规思路下,我们会这样实现let names = list.map(function(item) {return item.name;})
优化:let prop = curry(function(key,obj) {return obj[key];})let names = list.map(prop('name'))
延迟运行
ramda因为不是返回运算结果,而是返回新函数,所以是延迟运行。例如bind就是延迟执行的代表,不赘述
扁平化// 函数更加易读var curry = require('curry');var get = curry(function(property, object){ return object[property] });<br />fetchFromServer().then(JSON.parse).then(get('posts')).then(map(get('title')))
ramda文档归类
- 算术
// 加R.add(2, 3); //=> 5R.add(7)(10); //=> 17<br />// 自动加1R.inc(42); //=> 43<br />// 减R.subtract(10, 8); //=> 2const minus5 = R.subtract(R.__, 5);minus5(17); //=> 12<br />// 自动减1R.dec(42); //=> 41<br />// 乘const double = R.multiply(2);double(3); //=> 6<br />// 除R.divide(71, 100); //=> 0.71<br />// 累加R.sum([2,4,6,8,100,1]); //=> 121<br />//累乘R.product([2,4,6,8,100,1]); //=> 38400<br />// 算最大值R.max(789, 123); //=> 789R.max('a', 'b'); //=> 'b'<br />//包裹函数判断最大值const square = n => n * n;R.maxBy(square, -3, 2); //=> -3R.reduce(R.maxBy(square), 0, [3, -5, 4, 1, -2]); //=> -5<br />// 算最小值R.min(789, 123); //=> 123R.min('a', 'b'); //=> 'a'<br />//包裹函数判断最小值const square = n => n * n;R.minBy(square, -3, 2); //=> 2R.reduce(R.minBy(square), Infinity, [3, -5, 4, 1, -2]); //=> 1<br />// 返回给定数字列表的平均值。R.mean([2, 7, 9]); //=> 6R.mean([]); //=> NaN<br />// 返回给定数字列表的中位数。R.median([2, 9, 7]); //=> 7R.median([7, 2, 10, 9]); //=> 8R.median([]); //=> NaN<br />// mathMod 和算术取模操作类似,而不像 % 操作符 (或 [R.modulo](https://ramda.cn/docs/#modulo))。所以 -17 % 5 等于 -2,而 mathMod(-17, 5) 等于 3 。mathMod 要求参数为整型,并且当模数等于 0 或者负数时返回 NaN 。R.mathMod(-17, 5); //=> 3R.mathMod(17, 5); //=> 2R.mathMod(17, -5); //=> NaNR.mathMod(17, 0); //=> NaNR.mathMod(17.2, 5); //=> NaNR.mathMod(17, 5.3); //=> NaN<br />//用第一个参数除以第二个参数,并返回余数。注意,该函数是 JavaScript-style 的求模操作。数学求模另见 mathMod。R.modulo(17, 3); //=> 2// JS behavior:R.modulo(-17, 3); //=> -2R.modulo(17, -3); //=> 2const isOdd = R.modulo(R.__, 2);isOdd(42); //=> 0isOdd(21); //=> 1
- 判断
all, any,none,contains,includes,has,hasIn,hasPath,identical,is,isEmpty,isNil,where, whereEq// 如果列表中的所有元素都满足 predicate,则返回 true;否则,返回 falseconst equals3 = R.equals(3);R.all(equals3)([3, 3, 3, 3]); //=> true<br />const lessThan0 = R.flip(R.lt)(0);R.any(lessThan0)([1, 2]); //=> false<br />const isEven = n => n % 2 === 0;R.none(isEven, [1, 3, 5, 7, 9, 11]); //=> true<br />// 只要列表中有一个元素等于指定值,则返回 true;否则返回 false。通过 [R.equals](https://ramda.cn/docs/#equals) 函数进行相等性判断R.contains(3, [1, 2, 3]); //=> trueR.contains(4, [1, 2, 3]); //=> falseR.contains({ name: 'Fred' }, [{ name: 'Fred' }]); //=> trueR.contains([42], [[42]]); //=> trueR.contains('ba', 'banana'); //=>true<br />// 只要列表中有一个元素等于指定值,则返回 true;否则返回 false。通过 R.equals 函数进行相等性判断。R.includes(3, [1, 2, 3]); //=> trueR.includes(4, [1, 2, 3]); //=> falseR.includes({ name: 'Fred' }, [{ name: 'Fred' }]); //=> trueR.includes([42], [[42]]); //=> trueR.includes('ba', 'banana'); //=>true<br />// 如果对象自身含有指定的属性,则返回 true;否则返回 false。const hasName = R.has('name');hasName({name: 'alice'}); //=> truehasName({name: 'bob'}); //=> truehasName({}); //=> false<br />// 如果对象自身或其原型链上含有指定的属性,则返回 true;否则返回 false。function Rectangle(width, height) {this.width = width;this.height = height;}Rectangle.prototype.area = function() {return this.width * this.height;};<br />const square = new Rectangle(2, 2);R.hasIn('width', square); //=> trueR.hasIn('area', square); //=> true<br />// 检查对象中是否存在指定的路径。只检查对象自身的属性。R.hasPath(['a', 'b'], {a: {b: 2}}); // => trueR.hasPath(['a', 'b'], {a: {b: undefined}}); // => trueR.hasPath(['a', 'b'], {a: {c: 2}}); // => falseR.hasPath(['a', 'b'], {}); // => false<br />// 如果两个参数是完全相同,则返回 true,否则返回 false。如果它们引用相同的内存,也认为是完全相同的。NaN 和 NaN 是完全相同的;0 和 -0 不是完全相同的。const o = {};R.identical(o, o); //=> trueR.identical(1, 1); //=> trueR.identical(1, '1'); //=> falseR.identical([], []); //=> falseR.identical(0, -0); //=> falseR.identical(NaN, NaN); //=> true<br />// 检测一个对象(val)是否是给定构造函数的实例。该函数会依次检测其原型链,如果存在的话。R.is(Object, {}); //=> trueR.is(Number, 1); //=> trueR.is(Object, 1); //=> falseR.is(String, 's'); //=> trueR.is(String, new String('')); //=> trueR.is(Object, new String('')); //=> trueR.is(Object, 's'); //=> falseR.is(Number, {}); //=> false<br />//检测给定值是否为其所属类型的空值,若是则返回 true ;否则返回 false 。R.isEmpty([1, 2, 3]); //=> falseR.isEmpty([]); //=> trueR.isEmpty(''); //=> trueR.isEmpty(null); //=> falseR.isEmpty({}); //=> trueR.isEmpty({length: 0}); //=> false<br />// 检测输入值是否为 null 或 undefined 。R.isNil(null); //=> trueR.isNil(undefined); //=> trueR.isNil(0); //=> falseR.isNil([]); //=> false<br />//接受一个测试规范对象和一个待检测对象,如果测试满足规范,则返回 true,否则返回 false。测试规范对象的每个属性值都必须是 predicate 。每个 predicate 作用于待检测对象对应的属性值,如果所有 predicate 都返回 true,则 where 返回 true,否则返回 false 。const pred = R.where({a: R.equals('foo'),b: R.complement(R.equals('bar')),x: R.gt(R.__, 10),y: R.lt(R.__, 20)});pred({a: 'foo', b: 'xxx', x: 11, y: 19}); //=> truepred({a: 'xxx', b: 'xxx', x: 11, y: 19}); //=> falsepred({a: 'foo', b: 'bar', x: 11, y: 19}); //=> falsepred({a: 'foo', b: 'xxx', x: 10, y: 19}); //=> falsepred({a: 'foo', b: 'xxx', x: 11, y: 20}); //=> false<br />// 接受一个测试规范对象和一个待检测对象,如果测试满足规范,则返回 true,否则返回 false。如果对于每一个测试规范对象的属性值,待检测对象中都有一个对应的相同属性值,则 where 返回 true,否则返回 false 。const pred = R.whereEq({a: 1, b: 2});pred({a: 1}); //=> falsepred({a: 1, b: 2}); //=> truepred({a: 1, b: 2, c: 3}); //=> truepred({a: 1, b: 1}); //=> false
allPass, anyPass// 传入包含多个 predicate 的列表,返回一个 predicate:如果给定的参数满足列表中的所有 predicate ,则返回 trueconst isQueen = R.propEq('rank', 'Q');const isSpade = R.propEq('suit', '♠︎');const isQueenOfSpades = R.allPass([isQueen, isSpade]);isQueenOfSpades({rank: 'Q', suit: '♣︎'}); //=> falseisQueenOfSpades({rank: 'Q', suit: '♠︎'}); //=> true<br />const isClub = R.propEq('suit', '♣');const isSpade = R.propEq('suit', '♠');const isBlackCard = R.anyPass([isClub, isSpade]);isBlackCard({rank: '10', suit: '♣'}); //=> trueisBlackCard({rank: 'Q', suit: '♦'}); //=> false
endsWith, starsWith,eqBy, eqProps,equals// 检查列表是否以指定的子列表结尾R.endsWith('c', 'abc') //=> trueR.endsWith('b', 'abc') //=> falseR.endsWith(['c'], ['a', 'b', 'c']) //=> trueR.endsWith(['b'], ['a', 'b', 'c']) //=> false<br />// 检查列表是否以给定的值开头。R.startsWith('a', 'abc') //=> trueR.startsWith('b', 'abc') //=> falseR.startsWith(['a'], ['a', 'b', 'c']) //=> trueR.startsWith(['b'], ['a', 'b', 'c']) //=> false<br />// 接受一个函数和两个值,通过传入函数对两个值进行相等性判断。如果两个值的计算结果相等,则返回 true ;否则返回 false 。R.eqBy(Math.abs, 5, -5); //=> true<br />// 判断两个对象指定的属性值是否相等。通过 [R.equals](https://ramda.cn/docs/#equals) 函数进行相等性判断。可用作柯里化的 predicate 。const o1 = { a: 1, b: 2, c: 3, d: 4 };const o2 = { a: 10, b: 20, c: 3, d: 40 };R.eqProps('a', o1, o2); //=> falseR.eqProps('c', o1, o2); //=> true<br />// 如果传入的参数相等,返回 true;否则返回 false。可以处理几乎所有 JavaScript 支持的数据结构。R.equals(1, 1); //=> trueR.equals(1, '1'); //=> falseR.equals([1, 2, 3], [1, 2, 3]); //=> true
cond,ifElse, unless,when// 返回一个封装了 if / else,if / else, ... 逻辑的函数 fn。 R.cond 接受列表元素为 [predicate,transformer] 的列表。 fn 的所有参数顺次作用于每个 predicate,直到有一个返回 "truthy" 值,此时相应 transformer 对参数处理,并作为 fn 的结果返回。 如果没有 predicate 匹配,则 fn 返回 undefined。const fn = R.cond([[R.equals(0), R.always('water freezes at 0°C')],[R.equals(100), R.always('water boils at 100°C')],[R.T, temp => 'nothing special happens at ' + temp + '°C']]);fn(0); //=> 'water freezes at 0°C'fn(50); //=> 'nothing special happens at 50°C'fn(100); //=> 'water boils at 100°C'<br />// 根据 condition predicate 的返回值调用 onTrue 或 onFalse 函数。const incCount = R.ifElse(R.has('count'),R.over(R.lensProp('count'), R.inc),R.assoc('count', 1));incCount({}); //=> { count: 1 }incCount({ count: 1 }); //=> { count: 2 }<br />// 判断输入值是否满足 predicate,若不符合,则将输入值传给 whenFalseFn 处理,并将处理结果作为返回;若符合,则将输入值原样返回。let safeInc = R.unless(R.isNil, R.inc);safeInc(null); //=> nullsafeInc(1); //=> 2<br />// 判断输入值是否满足 predicate,若符合,则将输入值传给 whenTrueFn 处理,并将处理结果作为返回;若不符合,则将输入值原样返回。const truncate = R.when(R.propSatisfies(R.gt(R.__, 10), 'length'),R.pipe(R.take(10), R.append('…'), R.join('')));truncate('12345'); //=> '12345'truncate('0123456789ABC'); //=> '0123456789…'
gt,lt,gte,lteR.gt(2, 1); //=> trueR.gt(2, 2); //=> false<br />R.lt(2, 1); //=> falseR.lt(2, 2); //=> false<br />R.gte(2, 1); //=> trueR.gte(2, 2); //=> true<br />R.lte(2, 1); //=> falseR.lte(2, 2); //=> true
- 逻辑方法
//如果两个参数都是 true,则返回 true;否则返回 false, 相当于&&R.and(true, true); //=> trueR.and(true, false); //=> falseR.and(false, true); //=> falseR.and(false, false); //=> false<br />//如果其中一个参数为真,另一个参数为假,则返回true ;否则返回false,异或操作R.xor(true, true); //=> falseR.xor(true, false); //=> trueR.xor(false, true); //=> trueR.xor(false, false); //=> false<br />//只要有一个参数为真(truth-y),就返回 true;否则返回 false,相当于||R.or(true, true); //=> trueR.or(true, false); //=> trueR.or(false, true); //=> trueR.or(false, false); //=> false<br />// 该函数调用两个函数,并对两函数返回值进行与操作。若第一个函数结果为 false-y 值 (false, null, 0 等),则返回该结果,否则返回第二个函数的结果。注意,both 为短路操作,即如果第一个函数返回 false-y 值,则不会调用第二个函数const gt10 = R.gt(R.__, 10)const lt20 = R.lt(R.__, 20)const f = R.both(gt10, lt20);f(15); //=> truef(30); //=> false<br />// 返回由 || 运算符连接的两个函数的包装函数。如果两个函数中任一函数的执行结果为 truth-y,则返回其执行结果。 注意,这个是短路表达式,意味着如果第一个函数返回 truth-y 值的话,第二个函数将不会执行。const gt10 = x => x > 10;const even = x => x % 2 === 0;const f = R.either(gt10, even);f(101); //=> truef(8); //=> true<br />// 对函数的返回值取反。接受一个函数 f,返回一个新函数 g:在输入参数相同的情况下,若 f 返回 'true-y' ,则 g 返回 false-y ,反之亦然const isNotNil = R.complement(R.isNil);R.isNil(null); //=> trueisNotNil(null); //=> falseR.isNil(7); //=> falseisNotNil(7); //=> true<br />R.not(true); //=> falseR.not(false); //=> trueR.not(0); //=> trueR.not(1); //=> false<br />// 反操作R.negate(42); //=> -42
- 循环,过滤等
addIndex// 例如,addIndex 可以将 [R.map](https://ramda.cn/docs/#map) 转换为类似于 Array.prototype.map 的函数。注意,addIndex 只适用于迭代回调函数是首个参数、列表是最后一个参数的函const mapIndexed = R.addIndex(R.map);mapIndexed((val, idx) => idx + '-' + val, {x: 1, y: 2, z: 3});// {x: "0-1", y: "1-2", z: "2-3"}<br />const mapIndexed = R.addIndex(R.map);mapIndexed((val, idx) => idx + '-' + val, ['f', 'o', 'o', 'b', 'a', 'r']);//=> ['0-f', '1-o', '2-o', '3-b', '4-a', '5-r']
forEach, forEachObjIndexed,map, mapObjIndexed,mapAccum, mapAccumRight// 遍历 list,对 list 中的每个元素执行方法 fn。const printXPlusFive = x => console.log(x + 5);R.forEach(printXPlusFive, [1, 2, 3]); //=> [1, 2, 3]<br />// 遍历 object,对 object 中的每对 key 和 value 执行方法 fn。const printKeyConcatValue = (value, key) => console.log(key + ':' + value);R.forEachObjIndexed(printKeyConcatValue, {x: 1, y: 2}); //=> {x: 1, y: 2}// logs x:1// logs y:2<br />// 接收一个函数和一个 [functor](https://github.com/fantasyland/fantasy-land#functor), 将该函数应用到 functor 的每个值上,返回一个具有相同形态的 functor。const double = x => x * 2;R.map(double, [1, 2, 3]); //=> [2, 4, 6]R.map(double, {x: 1, y: 2, z: 3}); //=> {x: 2, y: 4, z: 6}<br />// Object 版本的 [map](https://ramda.cn/docs/#map)。mapping function 接受三个参数: _(value, key, obj)_ 。如果仅用到参数 _value_,则用 [map](https://ramda.cn/docs/#map) 即可。const xyz = { x: 1, y: 2, z: 3 };const prependKeyAndDouble = (num, key, obj) => key + (num * 2);R.mapObjIndexed(prependKeyAndDouble, xyz); //=> { x: 'x2', y: 'y4', z: 'z6' }<br />// mapAccum 的行为类似于 map 和 reduce 的组合;它将迭代函数作用于列表中的每个元素,从左往右传递经迭代函数计算的累积值,并将最后的累积值和由所有中间的累积值组成的列表一起返回。 迭代函数接收两个参数,_acc_ 和 _value_, 返回一个元组 _[acc, value]_。const digits = ['1', '2', '3', '4'];const appender = (a, b) => [a + b, a + b];R.mapAccum(appender, 0, digits); //=> ['01234', ['01', '012', '0123', '01234']]<br />// mapAccumRight 的行为类似于 map 和 reduce 的组合;它将迭代函数作用于列表中的每个元素,从右往左传递经迭代函数计算的累积值,并将最后的累积值和由所有中间的累积值组成的列表一起返回。和 [mapAccum](https://ramda.cn/docs/#mapAccum) 类似,除了列表遍历顺序是从右往左的。const digits = ['1', '2', '3', '4'];const appender = (a, b) => [b + a, b + a];R.mapAccumRight(appender, 5, digits); //=> ['12345', ['12345', '2345', '345', '45']]<br />
groupBy, groupWith,reduceBy, reduce, scan,reduceRight,reduceWhile,reduced,transduce// 对列表中的每个元素调用函数,根据函数返回结果进行分组。函数返回字符串作为相等性判断,返回的字符串作为存储对象的键,具有相同返回字符串的元素聚合为数组,作为该键的值。const byGrade = R.groupBy(function(student) {const score = student.score;return score < 65 ? 'F' :score < 70 ? 'D' :score < 80 ? 'C' :score < 90 ? 'B' : 'A';});const students = [{name: 'Abby', score: 84},{name: 'Eddy', score: 58},// ...{name: 'Jack', score: 69}];byGrade(students);// {// 'A': [{name: 'Dianne', score: 99}],// 'B': [{name: 'Abby', score: 84}]// // ...,// 'F': [{name: 'Eddy', score: 58}]// }<br />// 通过给定的对比函数,将列表按顺序分割成多组子列表。R.groupWith(R.equals, [0, 1, 1, 2, 3, 5, 8, 13, 21])//=> [[0], [1, 1], [2], [3], [5], [8], [13], [21]]<br />R.groupWith((a, b) => a + 1 === b, [0, 1, 1, 2, 3, 5, 8, 13, 21])//=> [[0, 1], [1, 2, 3], [5], [8], [13], [21]]<br />R.groupWith((a, b) => a % 2 === b % 2, [0, 1, 1, 2, 3, 5, 8, 13, 21])//=> [[0], [1, 1], [2], [3, 5], [8], [13, 21]]<br />R.groupWith(R.eqBy(isVowel), 'aestiou')//=> ['ae', 'st', 'iou']<br />// 首先对列表中的每个元素调用函数 keyFn ,根据 keyFn 返回的字符串对列表元素进行分组。然后调用 reducer 函数 valueFn,对组内的元素进行折叠操作。该函数相当于更通用的 [groupBy](https://ramda.cn/docs/#groupBy) 函数。const groupNames = (acc, {name}) => acc.concat(name)const toGrade = ({score}) =>score < 65 ? 'F' :score < 70 ? 'D' :score < 80 ? 'C' :score < 90 ? 'B' : 'A'<br />var students = [{name: 'Abby', score: 83},{name: 'Bart', score: 62},{name: 'Curt', score: 88},{name: 'Dora', score: 92},]<br />reduceBy(groupNames, [], toGrade, students)//=> {"A": ["Dora"], "B": ["Abby", "Curt"], "F": ["Bart"]}<br />// 遍历列表,相继调用二元迭代函数(参数为累积值和从数组中取出的当前元素),将本次迭代结果作为下次迭代的累积值。返回最终累积值。R.reduce(R.subtract, 0, [1, 2, 3, 4]) // => ((((0 - 1) - 2) - 3) - 4) = -10<br />// Scan 与 [reduce](https://ramda.cn/docs/#reduce) 类似,但会将每次迭代计算的累积值记录下来,组成一个列表返回。const numbers = [1, 2, 3, 4];const factorials = R.scan(R.multiply, 1, numbers); //=> [1, 1, 2, 6, 24]<br />// 遍历列表,相继调用二元迭代函数(参数为累积值和从数组中取出的当前元素),将本次迭代结果作为下次迭代的累积值。返回最终累积值。R.reduceRight(R.subtract, 0, [1, 2, 3, 4]) // => (1 - (2 - (3 - (4 - 0)))) = -2<br />// 与 [reduce](https://ramda.cn/docs/#reduce) 类似, reduceWhile 会遍历列表,相继调用二元迭代函数,并返回最终累积值。reduceWhile 在每次调用迭代函数前,先使用 predicate 进行判断,如果 predicate 返回 false ,则提前终止遍历操作,并返回当前累积值。const isOdd = (acc, x) => x % 2 === 1;const xs = [1, 3, 5, 60, 777, 800];R.reduceWhile(isOdd, R.add, 0, xs); //=> 9<br />const ys = [2, 4, 6]R.reduceWhile(isOdd, R.add, 111, ys); //=> 111<br />// 返回一个封装的值,该值代表 reduce 或 transduce 操作的最终结果。R.reduce((acc, item) => item > 3 ? R.reduced(acc) : acc.concat(item),[],[1, 2, 3, 4, 5]) // [1, 2, 3]<br />// 用 iterator function 初始化 transducer ,生成一个 transformed iterator function。然后顺次遍历列表,对每个列表元素先进行转换,然后与累积值进行归约,返回值作为下一轮迭代的累积值。最终返回与初始累积值类型相同的一个累积值。const numbers = [1, 2, 3, 4];const transducer = R.compose(R.map(R.add(1)), R.take(2));R.transduce(transducer, R.flip(R.append), [], numbers); //=> [2, 3]<br />const isOdd = (x) => x % 2 === 1;const firstOddTransducer = R.compose(R.filter(isOdd), R.take(1));R.transduce(firstOddTransducer, R.flip(R.append), [], R.range(0, 100)); //=> [1]
into, unfold// accumulator 的类型可以是:array、string、object 或者 transformer 。如果 accumulator 类型是 array 或 string,则迭代元素将被添加到数组或连接到字符串上;如果是对象,迭代元素将会被直接合并;如果是二元素数组,迭代元素会以键值对形式进行合并。const numbers = [1, 2, 3, 4];const transducer = R.compose(R.map(R.add(1)), R.take(2));<br />R.into([], transducer, numbers); //=> [2, 3]<br />const intoArray = R.into([]);intoArray(transducer, numbers); //=> [2, 3]<br />// 通过一个种子值( seed )创建一个列表。unfold 接受一个迭代函数:该函数或者返回 false 停止迭代,或者返回一个长度为 2 的数组:数组首个元素添加到结果列表,第二个元素作为种子值传给下一轮迭代使用。const f = n => n > 50 ? false : [-n, n + 10];R.unfold(f, 10); //=> [-10, -20, -30, -40, -50]
filter,reject,partition,innerJoin,// 使用 predicate 遍历传入的 Filterable,返回满足 predicate 的所有元素的新的 Filterable。新 Filterable 与原先的类型相同。Filterable 类型包括 plain object 或者任何带有 filter 方法的类型,如 Array 。const isEven = n => n % 2 === 0;R.filter(isEven, [1, 2, 3, 4]); //=> [2, 4]R.filter(isEven, {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, d: 4}<br />// [filter](https://ramda.cn/docs/#filter) 的补操作。返回结果为 [R.filter](https://ramda.cn/docs/#filter) 操作结果的补集。const isOdd = (n) => n % 2 === 1;R.reject(isOdd, [1, 2, 3, 4]); //=> [2, 4]R.reject(isOdd, {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, d: 4}<br />// 通过 predicate 将列表或 "Filterable" (可过滤的)对象分成两部分,分别为满足 predicate 的元素和不满足 predicate 的元素。元素类型保持不变。Filterable 类型包括 plain object 或者任何带有 filter 方法的类型,如 Array 。R.partition(R.includes('s'), ['sss', 'ttt', 'foo', 'bars']);// => [ [ 'sss', 'bars' ], [ 'ttt', 'foo' ] ]R.partition(R.includes('s'), { a: 'sss', b: 'ttt', foo: 'bars' });// => [ { a: 'sss', foo: 'bars' }, { b: 'ttt' } ]<br />// 接受一个 predicate pred 、列表 xs 和 ys ,返回列表 xs'。依次取出 xs 中的元素,若通过 pred 判断等于 ys 中的一个或多个元素,则放入 xs' 。pred 必须为二元函数,两个参数分别来自于对应两个列表中的元素。R.innerJoin((record, id) => record.id === id,[{id: 824, name: 'Richie Furay'},{id: 956, name: 'Dewey Martin'},{id: 313, name: 'Bruce Palmer'},{id: 456, name: 'Stephen Stills'},{id: 177, name: 'Neil Young'}],[177, 456, 999]);//=> [{id: 456, name: 'Stephen Stills'}, {id: 177, name: 'Neil Young'}]
- 改变对象,函数转化
adjust, update,insert, insertAll,remove,intersperse,move// 将数组中指定索引处的值替换为经函数变换的值R.adjust(1, R.toUpper, ['a', 'b', 'c', 'd']); //=> ['a', 'B', 'c', 'd']<br />R.update(1, '_', ['a', 'b', 'c']); //=> ['a', '_', 'c']<br />// 将元素插入到 list 指定索引处。注意,该函数是非破坏性的:返回处理后列表的拷贝。函数运行过程中不会破坏任何列表。R.insert(2, 'x', [1,2,3,4]); //=> [1,2,'x',3,4]<br />//将子 list 插入到 list 指定索引处。注意,该函数是非破坏性的:返回处理后列表的拷贝。函数运行过程中不会破坏任何列表。R.insertAll(2, ['x','y','z'], [1,2,3,4]); //=> [1,2,'x','y','z',3,4]<br />//删除列表中从 start 开始的 count 个元素。注意,该操作是非破坏性的:不改变原列表,返回处理后列表的拷贝。R.remove(2, 3, [1,2,3,4,5,6,7,8]); //=> [1,2,6,7,8]<br />// 在列表的元素之间插入分割元素。R.intersperse('a', ['b', 'n', 'n', 's']); //=> ['b', 'a', 'n', 'a', 'n', 'a', 's']<br />// 将列表中 from 索引处的元素移动到索引 to 处。R.move(0, 2, ['a', 'b', 'c', 'd', 'e', 'f']); //=> ['b', 'c', 'a', 'd', 'e', 'f']R.move(-1, 0, ['a', 'b', 'c', 'd', 'e', 'f']); //=> ['f', 'a', 'b', 'c', 'd', 'e'] list rotation
merge,mergeAll, mergeRight, mergeDeepRight, mergeLeft, mergeDeepLeft,mergeWith, mergeDeepWith, mergeWithKey, mergeDeepWithKey// 合并两个对象的自身属性(不包括 prototype 属性)。如果某个 key 在两个对象中都存在,使用后一个对象对应的属性值。R.merge({ 'name': 'fred', 'age': 10 }, { 'age': 40 });//=> { 'name': 'fred', 'age': 40 }const withDefaults = R.merge({x: 0, y: 0});withDefaults({y: 2}); //=> {x: 0, y: 2}<br />//将对象类型列表合并为一个对象。R.mergeAll([{foo:1},{bar:2},{baz:3}]); //=> {foo:1,bar:2,baz:3}R.mergeAll([{foo:1},{foo:2},{bar:2}]); //=> {foo:2,bar:2}<br />// 合并两个对象的自身属性(不包括 prototype 属性)。如果某个 key 在两个对象中都存在,使用后一个对象对应的属性值。R.mergeRight({ 'name': 'fred', 'age': 10 }, { 'age': 40 });//=> { 'name': 'fred', 'age': 40 }const withDefaults = R.mergeRight({x: 0, y: 0});withDefaults({y: 2}); //=> {x: 0, y: 2}<br />// 合并两个对象的自身属性(不包括 prototype 属性)。如果某个 key 在两个对象中都存在:并且两个值都是对象,则继续递归合并这两个值;否则,采用第二个对象的值。R.mergeDeepRight({ name: 'fred', age: 10, contact: { email: 'moo@example.com' }},{ age: 40, contact: { email: 'baa@example.com' }});//=> { name: 'fred', age: 40, contact: { email: 'baa@example.com' }}<br />// 合并两个对象的自身属性(不包括 prototype 属性)。如果某个 key 在两个对象中都存在,使用前一个对象对应的属性值。R.mergeLeft({ 'age': 40 }, { 'name': 'fred', 'age': 10 });//=> { 'name': 'fred', 'age': 40 }const resetToDefault = R.mergeLeft({x: 0});resetToDefault({x: 5, y: 2}); //=> {x: 0, y: 2}<br />// 合并两个对象的自身属性(不包括 prototype 属性)。如果某个 key 在两个对象中都存在:并且两个值都是对象,则继续递归合并这两个值;否则,采用第一个对象的值。R.mergeDeepLeft({ name: 'fred', age: 10, contact: { email: 'moo@example.com' }},{ age: 40, contact: { email: 'baa@example.com' }});//=> { name: 'fred', age: 10, contact: { email: 'moo@example.com' }}<br />// 使用给定的两个对象自身属性(不包括 prototype 属性)来创建一个新对象。如果某个 key 在两个对象中都存在,则使用给定的函数对每个对象该 key 对应的 value 进行处理,处理结果作为新对象该 key 对应的值。R.mergeWith(R.concat,{ a: true, values: [10, 20] },{ b: true, values: [15, 35] });//=> { a: true, b: true, values: [10, 20, 15, 35] }<br />// 合并两个对象的自身属性(不包括 prototype 属性)。如果某个 key 在两个对象中都存在:并且两个关联的值都是对象,则继续递归合并这两个值;否则,使用给定函数对两个值进行处理,并将返回值作为该 key 的新值。R.mergeDeepWith(R.concat,{ a: true, c: { values: [10, 20] }},{ b: true, c: { values: [15, 35] }});//=> { a: true, b: true, c: { values: [10, 20, 15, 35] }}<br />// 使用给定的两个对象自身属性(不包括 prototype 属性)来创建一个新对象。如果某个 key 在两个对象中都存在,则使用给定的函数对该 key 和每个对象该 key 对应的 value 进行处理,处理结果作为新对象该 key 对应的值。let concatValues = (k, l, r) => k == 'values' ? R.concat(l, r) : rR.mergeWithKey(concatValues,{ a: true, thing: 'foo', values: [10, 20] },{ b: true, thing: 'bar', values: [15, 35] });//=> { a: true, b: true, thing: 'bar', values: [10, 20, 15, 35] }<br />// 合并两个对象的自身属性(不包括 prototype 属性)。如果某个 key 在两个对象中都存在:并且两个关联的值都是对象,则继续递归合并这两个值;否则,使用给定函数对该 key 和对应的两个值进行处理,并将返回值作为该 key 的新值。let concatValues = (k, l, r) => k == 'values' ? R.concat(l, r) : rR.mergeDeepWithKey(concatValues,{ a: true, c: { thing: 'foo', values: [10, 20] }},{ b: true, c: { thing: 'bar', values: [15, 35] }});//=> { a: true, b: true, c: { thing: 'bar', values: [10, 20, 15, 35] }}
applySpec//接受一个属性值为函数的对象,返回一个能生成相同结构对象的函数。返回的函数使用传入的参数调用对象的每个属性位对应的函数,来生成相应属性的值。const getMetrics = R.applySpec({sum: R.add,nested: { mul: R.multiply }});getMetrics(2, 4); // => { sum: 6, nested: { mul: 8 } }
evolve// 递归地对 object 的属性进行变换,变换方式由 transformation 函数定义。所有非原始类型属性都通过引用来复制。const tomato = {firstName: ' Tomato ', data: {elapsed: 100, remaining: 1400}, id:123};const transformations = {firstName: R.trim,lastName: R.trim, // Will not get invoked.data: {elapsed: R.add(1), remaining: R.add(-1)}};R.evolve(transformations, tomato); //=> {firstName: 'Tomato', data: {elapsed: 101, remaining: 1399}, id:123}
converge, useWith,ap,lift, liftN// 接受一个 converging 函数和一个分支函数列表,返回一个新函数。新函数的元数(参数个数)等于最长分支函数的元数。当被调用时,新函数接受参数,并将这些参数转发给每个分支函数;然后将每个分支函数的计算结果作为参数传递给 converging 函数,converging 函数的计算结果即新函数的返回值const average = R.converge(R.divide, [R.sum, R.length])average([1, 2, 3, 4, 5, 6, 7]) //=> 4<br />const strangeConcat = R.converge(R.concat, [R.toUpper, R.toLower])strangeConcat("Yodel") //=> "YODELyodel"<br />// 接受一个函数 fn 和一个 transformer 函数的列表,返回一个柯里化的新函数。当被调用时,新函数将每个参数转发给对应位置的 transformer 函数,然后将每个 transformer 函数的计算结果作为参数传递给 fn,fn 的计算结果即新函数的返回值。R.useWith(Math.pow, [R.identity, R.identity])(3, 4); //=> 81R.useWith(Math.pow, [R.identity, R.identity])(3)(4); //=> 81R.useWith(Math.pow, [R.dec, R.inc])(3, 4); //=> 32R.useWith(Math.pow, [R.dec, R.inc])(3)(4); //=> 32<br />// ap 将函数列表作用于值列表上。R.ap([R.multiply(2), R.add(3)], [1,2,3]); //=> [2, 4, 6, 4, 5, 6]R.ap([R.concat('tasty '), R.toUpper], ['pizza', 'salad']); //=> ["tasty pizza", "tasty salad", "PIZZA", "SALAD"]R.ap(R.concat, R.toUpper)('Ramda') //=> 'RamdaRAMDA'<br />// 提升一个多元函数,使之能映射到列表、函数或其他符合 [FantasyLand Apply spec](https://github.com/fantasyland/fantasy-land#apply) 规范的对象上const madd3 = R.lift((a, b, c) => a + b + c);madd3([1,2,3], [1,2,3], [1]); //=> [3, 4, 5, 4, 5, 6, 5, 6, 7]const madd5 = R.lift((a, b, c, d, e) => a + b + c + d + e);madd5([1,2], [3], [4, 5], [6], [7, 8]); //=> [21, 22, 22, 23, 22, 23, 23, 24]<br />//将一个函数提升为指定元数的函数,使之能映射到多个列表、函数或其他符合 [FantasyLand Apply spec](https://github.com/fantasyland/fantasy-land#apply) 规范的对象上。const madd3 = R.liftN(3, (...args) => R.sum(args));madd3([1,2,3], [1,2,3], [1]); //=> [3, 4, 5, 4, 5, 6, 5, 6, 7]
juxtconst getRange = R.juxt([Math.min, Math.max]);getRange(3, 4, 9, -3); //=> [-3, 9]
assoc, assocPath,dissoc, dissocPath ,omit, pick, pickBy,pickAll,drop, dropLast, dropLastWhile, dropWhile ,take, takeLast, takeLastWhile, takeWhile,pluck,project// 浅复制对象,然后设置或覆盖对象的指定属性R.assoc('c', 3, {a: 1, b: 2}); //=> {a: 1, b: 2, c: 3}<br />R.assocPath(['a', 'b', 'c'], 42, {a: {b: {c: 0}}}); //=> {a: {b: {c: 42}}}R.assocPath(['a', 'b', 'c'], 42, {a: 5}); //=> {a: {b: {c: 42}}}<br />R.dissoc('b', {a: 1, b: 2, c: 3}); //=> {a: 1, c: 3}<br />R.dissocPath(['a', 'b', 'c'], {a: {b: {c: 42}}}); //=> {a: {b: {}}}<br />R.omit(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, c: 3}<br />// 返回对象的部分拷贝,其中仅包含指定键对应的属性。如果某个键不存在,则忽略该属性。R.pick(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {a: 1, d: 4}R.pick(['a', 'e', 'f'], {a: 1, b: 2, c: 3, d: 4}); //=> {a: 1}<br />// 返回对象的部分拷贝,其中仅包含 key 满足 predicate 的属性。const isUpperCase = (val, key) => key.toUpperCase() === key;R.pickBy(isUpperCase, {a: 1, b: 2, A: 3, B: 4}); //=> {A: 3, B: 4}<br />//与 pick 类似,但 pickAll 会将不存在的属性以 key: undefined 键值对的形式返回R.pickAll(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {a: 1, d: 4}R.pickAll(['a', 'e', 'f'], {a: 1, b: 2, c: 3, d: 4}); //=> {a: 1, e: undefined, f: undefined}<br />// 删除给定 list,string 或者 transducer/transformer(或者具有 drop 方法的对象)的前 n 个元素。R.drop(1, ['foo', 'bar', 'baz']); //=> ['bar', 'baz']R.drop(2, ['foo', 'bar', 'baz']); //=> ['baz']R.drop(3, ['foo', 'bar', 'baz']); //=> []R.drop(3, 'ramda'); //=> 'da'<br />// 删除 "list" 末尾的 n 个元素。R.dropLast(1, ['foo', 'bar', 'baz']); //=> ['foo', 'bar']R.dropLast(2, ['foo', 'bar', 'baz']); //=> ['foo']R.dropLast(3, ['foo', 'bar', 'baz']); //=> []R.dropLast(3, 'ramda'); //=> 'ra'<br />// 对 list 从后向前一直删除满足 predicate 的尾部元素,直到遇到第一个 falsy 值,此时停止删除操作。const lteThree = x => x <= 3;R.dropLastWhile(lteThree, [1, 2, 3, 4, 3, 2, 1]); //=> [1, 2, 3, 4]R.dropLastWhile(x => x !== 'd' , 'Ramda'); //=> 'Ramd'<br />// 对 list 从前向后删除满足 predicate 的头部元素,直到遇到第一个 falsy 值。const lteTwo = x => x <= 2;R.dropWhile(lteTwo, [1, 2, 3, 4, 3, 2, 1]); //=> [3, 4, 3, 2, 1]R.dropWhile(x => x !== 'd' , 'Ramda'); //=> 'da'<br />// 返回列表的前 n 个元素、字符串的前n个字符或者用作 transducer/transform(或者调用对象的 take 方法)R.take(1, ['foo', 'bar', 'baz']); //=> ['foo']R.take(2, ['foo', 'bar', 'baz']); //=> ['foo', 'bar']R.take(3, ['foo', 'bar', 'baz']); //=> ['foo', 'bar', 'baz']R.take(3, 'ramda'); //=> 'ram'<br />// 返回列表的后 n 个元素。如果 n > list.length,则返回 list.length 个元素。R.takeLast(1, ['foo', 'bar', 'baz']); //=> ['baz']R.takeLast(2, ['foo', 'bar', 'baz']); //=> ['bar', 'baz']R.takeLast(3, ['foo', 'bar', 'baz']); //=> ['foo', 'bar', 'baz']R.takeLast(3, 'ramda'); //=> 'mda'<br />// 从后往前取出列表元素,直到遇到首个不满足 predicate 的元素为止。取出的元素中不包含首个不满足 predicate 的元素。const isNotOne = x => x !== 1;R.takeLastWhile(isNotOne, [1, 2, 3, 4]); //=> [2, 3, 4]R.takeLastWhile(x => x !== 'R' , 'Ramda'); //=> 'amda'<br />// 从前往后取出列表元素,直到遇到首个不满足 predicate 的元素为止。取出的元素中不包含首个不满足 predicate 的元素。const isNotFour = x => x !== 4;R.takeWhile(isNotFour, [1, 2, 3, 4, 3, 2, 1]); //=> [1, 2, 3]R.takeWhile(x => x !== 'd' , 'Ramda'); //=> 'Ram'<br />// 从列表内的每个对象元素中取出特定名称的属性,组成一个新的列表。var getAges = R.pluck('age');getAges([{name: 'fred', age: 29}, {name: 'wilma', age: 27}]); //=> [29, 27]R.pluck(0, [[1, 2], [3, 4]]); //=> [1, 3]R.pluck('val', {a: {val: 3}, b: {val: 5}}); //=> {a: 3, b: 5}<br />// 模拟 SQL 中的 select 语句。const abby = {name: 'Abby', age: 7, hair: 'blond', grade: 2};const fred = {name: 'Fred', age: 12, hair: 'brown', grade: 7};const kids = [abby, fred];R.project(['name', 'grade'], kids); //=> [{name: 'Abby', grade: 2}, {name: 'Fred', grade: 7}]
lens, lensIndex, lensProp//返回封装了给定 getter 和 setter 方法的 lens 。 getter 和 setter 分别用于 “获取” 和 “设置” 焦点(lens 聚焦的值)。setter 不会改变原数据。const xLens = R.lens(R.prop('x'), R.assoc('x'));R.view(xLens, {x: 1, y: 2}); //=> 1R.set(xLens, 4, {x: 1, y: 2}); //=> {x: 4, y: 2}R.over(xLens, R.negate, {x: 1, y: 2}); //=> {x: -1, y: 2}<br />// 返回聚焦到指定索引的 lens。const headLens = R.lensIndex(0);R.view(headLens, ['a', 'b', 'c']); //=> 'a'R.set(headLens, 'x', ['a', 'b', 'c']); //=> ['x', 'b', 'c']R.over(headLens, R.toUpper, ['a', 'b', 'c']); //=> ['A', 'b', 'c']<br />//返回聚焦到指定属性的 lens。const xLens = R.lensProp('x');R.view(xLens, {x: 1, y: 2}); //=> 1R.set(xLens, 4, {x: 1, y: 2}); //=> {x: 4, y: 2}R.over(xLens, R.negate, {x: 1, y: 2}); //=> {x: -1, y: 2}<br />// 返回聚焦到指定路径的 lens。const xHeadYLens = R.lensPath(['x', 0, 'y']);R.view(xHeadYLens, {x: [{y: 2, z: 3}, {y: 4, z: 5}]});//=> 2R.set(xHeadYLens, 1, {x: [{y: 2, z: 3}, {y: 4, z: 5}]});//=> {x: [{y: 1, z: 3}, {y: 4, z: 5}]}R.over(xHeadYLens, R.negate, {x: [{y: 2, z: 3}, {y: 4, z: 5}]});//=> {x: [{y: -2, z: 3}, {y: 4, z: 5}]}
view,set,over,prop, props, propsOr,propIs, propEq ,propSatisfies,path, paths,pathOr,pathEq, pathSatisfies// 返回数据结构中,lens 聚焦的部分。lens 的焦点决定了数据结构中的哪部分是可见的。const xLens = R.lensProp('x');R.view(xLens, {x: 1, y: 2}); //=> 1R.view(xLens, {x: 4, y: 2}); //=> 4<br />//通过 lens 对数据结构聚焦的部分进行设置const xLens = R.lensProp('x');R.set(xLens, 4, {x: 1, y: 2}); //=> {x: 4, y: 2}R.set(xLens, 8, {x: 1, y: 2}); //=> {x: 8, y: 2}<br />// 对数据结构中被 lens 聚焦的部分进行函数变换const headLens = R.lensIndex(0);R.over(headLens, R.toUpper, ['foo', 'bar', 'baz']); //=> ['FOO', 'bar', 'baz']<br />// 取出对象中指定属性的值。如果不存在,则返回 undefined。R.prop('x', {x: 100}); //=> 100R.prop('x', {}); //=> undefinedR.prop(0, [100]); //=> 100R.compose(R.inc, R.prop('x'))({ x: 3 }) //=> 4<br />// 返回 prop 的数组:输入为 keys 数组,输出为对应的 values 数组。values 数组的顺序与 keys 的相同。R.props(['x', 'y'], {x: 1, y: 2}); //=> [1, 2]R.props(['c', 'a', 'b'], {b: 2, a: 1}); //=> [undefined, 1, 2]const fullName = R.compose(R.join(' '), R.props(['first', 'last']));fullName({last: 'Bullet-Tooth', age: 33, first: 'Tony'}); //=> 'Tony Bullet-Tooth'<br />// 对于给定的非空对象,如果指定属性存在,则返回该属性值;否则返回给定的默认值。const alice = {name: 'ALICE',age: 101};const favorite = R.prop('favoriteLibrary');const favoriteWithDefault = R.propOr('Ramda', 'favoriteLibrary');favorite(alice); //=> undefinedfavoriteWithDefault(alice); //=> 'Ramda'<br />// 判断指定对象的属性是否为给定的数据类型,是则返回 true ;否则返回 false 。R.propIs(Number, 'x', {x: 1, y: 2}); //=> trueR.propIs(Number, 'x', {x: 'foo'}); //=> falseR.propIs(Number, 'x', {}); //=> false<br />// 如果指定对象属性与给定的值相等,则返回 true ;否则返回 false 。通过 [R.equals](https://ramda.cn/docs/#equals) 函数进行相等性判断。可以使用 [R.whereEq](https://ramda.cn/docs/#whereEq) 进行多个属性的相等性判断。const abby = {name: 'Abby', age: 7, hair: 'blond'};const fred = {name: 'Fred', age: 12, hair: 'brown'};const rusty = {name: 'Rusty', age: 10, hair: 'brown'};const alois = {name: 'Alois', age: 15, disposition: 'surly'};const kids = [abby, fred, rusty, alois];const hasBrownHair = R.propEq('hair', 'brown');R.filter(hasBrownHair, kids); //=> [fred, rusty]<br />// 如果指定的对象属性满足 predicate,返回 true;否则返回 false。可以使用 [R.where](https://ramda.cn/docs/#where) 进行多个属性的判断。R.propSatisfies(x => x > 0, 'x', {x: 1, y: 2}); //=> true<br />// 取出给定路径上的值。R.path(['a', 'b'], {a: {b: 2}}); //=> 2R.path(['a', 'b'], {c: {b: 2}}); //=> undefinedR.path(['a', 'b', 0], {a: {b: [1, 2, 3]}}); //=> 1R.path(['a', 'b', -2], {a: {b: [1, 2, 3]}}); //=> 2<br />// 提取对象中指定路径数组(paths)上的对应的值(values)R.paths([['a', 'b'], ['p', 0, 'q']], {a: {b: 2}, p: [{q: 3}]}); //=> [2, 3]R.paths([['a', 'b'], ['p', 'r']], {a: {b: 2}, p: [{q: 3}]}); //=> [2, undefined]<br />//如果非空对象在给定路径上存在值,则将该值返回;否则返回给定的默认值。R.pathOr('N/A', ['a', 'b'], {a: {b: 2}}); //=> 2R.pathOr('N/A', ['a', 'b'], {c: {b: 2}}); //=> "N/A"<br />// 判断对象的嵌套路径上是否为给定的值,通过 [R.equals](https://ramda.cn/docs/#equals) 函数进行相等性判断。常用于列表过滤。const user1 = { address: { zipCode: 90210 } };const user2 = { address: { zipCode: 55555 } };const user3 = { name: 'Bob' };const users = [ user1, user2, user3 ];const isFamous = R.pathEq(['address', 'zipCode'], 90210);R.filter(isFamous, users); //=> [ user1 ]<br />//如果对象的给定路径上的属性满足 predicate,返回 ture;否则返回 false。R.pathSatisfies(y => y > 0, ['x', 'y'], {x: {y: 2}}); //=> trueR.pathSatisfies(R.is(Object), [], {x: {y: 2}}); //=> true<br />
indexBy,invert, invertObj// 通过生成键的函数,将元素为对象的 list 转换为以生成的键为索引的新对象。注意,如果 list 中多个对象元素生成相同的键,以最后一个对象元素作为该键的值。const list = [{id: 'xyz', title: 'A'}, {id: 'abc', title: 'B'}];R.indexBy(R.prop('id'), list);//=> {abc: {id: 'abc', title: 'B'}, xyz: {id: 'xyz', title: 'A'}}<br />// 与 [R.invertObj](https://ramda.cn/docs/#invertObj) 类似,但会将值放入数组中,来处理一个键对应多个值的情况。const raceResultsByFirstName = {first: 'alice',second: 'jake',third: 'alice',};R.invert(raceResultsByFirstName);//=> { 'alice': ['first', 'third'], 'jake':['second'] }<br />// 将对象的键、值交换位置:值作为键,对应的键作为值。交换后的键会被强制转换为字符串。注意,如果原对象同一值对应多个键,采用最后遍历到的键。const raceResults = {first: 'alice',second: 'jake'};R.invertObj(raceResults);//=> { 'alice': 'first', 'jake':'second' }const raceResults = ['alice', 'jake'];R.invertObj(raceResults);//=> { 'alice': '0', 'jake':'1' }
fromPairs,toPairs,pair,objOf,of// 由一系列 “键值对” 创建一个 object。如果某个键出现多次,选取最右侧的键值对。R.fromPairs([['a', 1], ['b', 2], ['c', 3]]); //=> {a: 1, b: 2, c: 3}<br />// 将一个对象的属性转换成键、值二元组类型的数组,只处理对象自身的属性。注意:不同 JS 运行环境输出数组的顺序可能不一致。R.toPairs({a: 1, b: 2, c: 3}); //=> [['a', 1], ['b', 2], ['c', 3]]<br />// 将一个对象的属性转换成键、值二元组类型的数组,包括原型链上的属性。注意,不同 JS 运行环境输出数组的顺序可能不一致。const F = function() { this.x = 'X'; };F.prototype.y = 'Y';const f = new F();R.toPairsIn(f); //=> [['x','X'], ['y','Y']]<br />// 接收两个参数,fst 和 snd,返回数组 [fst, snd]。R.pair('foo', 'bar'); //=> ['foo', 'bar']<br />// 创建一个包含单个键值对的对象。const matchPhrases = R.compose(R.objOf('must'),R.map(R.objOf('match_phrase')));matchPhrases(['foo', 'bar', 'baz']); //=> {must: [{match_phrase: 'foo'}, {match_phrase: 'bar'}, {match_phrase: 'baz'}]}<br />// 将给定值作为元素,封装成单元素数组。R.of(null); //=> [null]R.of([42]); //=> [[42]]
- 数组或字符串方法
aperture, splitAt,splitEvery, splitWhen// 返回一个新列表,列表中的元素为由原列表相邻元素组成的 n 元组。如果 n 大于列表的长度,则返回空列表。R.aperture(2, [1, 2, 3, 4, 5]); //=> [[1, 2], [2, 3], [3, 4], [4, 5]]R.aperture(3, [1, 2, 3, 4, 5]); //=> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]R.aperture(7, [1, 2, 3, 4, 5]); //=> []<br />// 在指定的索引处拆分列表或者字符串。R.splitAt(1, [1, 2, 3]); //=> [[1], [2, 3]]R.splitAt(5, 'hello world'); //=> ['hello', ' world']R.splitAt(-1, 'foobar'); //=> ['fooba', 'r']<br />// 将列表拆分成指定长度的子列表集。R.splitEvery(3, [1, 2, 3, 4, 5, 6, 7]); //=> [[1, 2, 3], [4, 5, 6], [7]]R.splitEvery(3, 'foobarbaz'); //=> ['foo', 'bar', 'baz']<br />// 查找列表中首个满足 predicate 的元素,在该处将列表拆分为两部分。首个满足 predicate 的元素包含在后一部分R.splitWhen(R.equals(2), [1, 2, 3, 1, 2, 3]); //=> [[1], [2, 3, 1, 2, 3]]
append, prependR.append('tests', ['write', 'more']); //=> ['write', 'more', 'tests']<br />R.prepend('fee', ['fi', 'fo', 'fum']); //=> ['fee', 'fi', 'fo', 'fum']
countBy// 根据给定函数提供的统计规则对列表中的元素进行分类计数。返回一个对象,其键值对为:fn 根据列表元素生成键,列表中通过 fn 映射为对应键的元素的个数作为值。注意,由于 JavaScript 对象的实现方式,所有键都被强制转换为字符串。const numbers = [1.0, 1.1, 1.2, 2.0, 3.0, 2.2];R.countBy(Math.floor)(numbers); //=> {'1': 3, '2': 2, '3': 1}<br />const letters = ['a', 'b', 'A', 'a', 'B', 'c'];R.countBy(R.toLower)(letters); //=> {'a': 3, 'b': 2, 'c': 1}
difference, differenceWith, intersection,symmetricDifference, symmetricDifferenceWith,without,dropRepeats, uniq, uniqBy, uniqWith,
union, unionWith,dropRepeatsWith// 求差集。求第一个列表中,未包含在第二个列表中的任一元素的集合。对象和数组比较数值相等,而非引用相等。R.difference([1,2,3,4], [7,6,5,4,3]); //=> [1,2]R.difference([7,6,5,4,3], [1,2,3,4]); //=> [7,6,5]R.difference([{a: 1}, {b: 2}], [{a: 1}, {c: 3}]) //=> [{b: 2}]<br />// 求第一个列表中未包含在第二个列表中的所有元素的集合(集合中没有重复元素)。两列表中的元素通过 predicate 判断相应元素是否同时 “包含在” 两列表中。const cmp = (x, y) => x.a === y.a;const l1 = [{a: 1}, {a: 2}, {a: 3}];const l2 = [{a: 3}, {a: 4}];R.differenceWith(cmp, l1, l2); //=> [{a: 1}, {a: 2}]<br />//取出两个 list 中相同的元素组成的 set (集合:没有重复元素)R.intersection([1,2,3,4], [7,6,5,4,3]); //=> [4, 3]<br />// 求对称差集。所有不属于两列表交集元素的集合,其元素在且仅在给定列表中的一个里面出现。R.symmetricDifference([1,2,3,4], [7,6,5,4,3]); //=> [1,2,7,6,5]R.symmetricDifference([7,6,5,4,3], [1,2,3,4]); //=> [7,6,5,1,2]<br />// 求对称差集。所有不属于两列表交集元素的集合。交集的元素由条件函数的返回值决定。const eqA = R.eqBy(R.prop('a'));const l1 = [{a: 1}, {a: 2}, {a: 3}, {a: 4}];const l2 = [{a: 3}, {a: 4}, {a: 5}, {a: 6}];R.symmetricDifferenceWith(eqA, l1, l2); //=> [{a: 1}, {a: 2}, {a: 5}, {a: 6}]<br />// 求第二个列表中,未包含在第一个列表中的任一元素的集合。通过 [R.equals](https://ramda.cn/docs/#equals) 函数进行相等性判断。R.without([1, 2], [1, 2, 1, 3, 4]); //=> [3, 4]<br />// 返回一个没有连续重复元素的 list。通过 [R.equals](https://ramda.cn/docs/#equls) 函数进行相等性判断。R.dropRepeats([1, 1, 1, 2, 3, 4, 4, 2, 2]); //=> [1, 2, 3, 4, 2]<br />// 列表去重操作。返回无重复元素的列表。通过 [R.equals](https://ramda.cn/docs/#equals) 函数进行相等性判断。R.uniq([1, 1, 2, 1]); //=> [1, 2]R.uniq([1, '1']); //=> [1, '1']R.uniq([[42], [42]]); //=> [[42]]<br />// 返回无重复元素的列表。元素通过给定的函数的返回值以及 [R.equals](https://ramda.cn/docs/#equals) 进行相同性判断。如果给定的函数返回值相同,保留第一个元素。R.uniqBy(Math.abs, [-1, -5, 2, 10, 1, 2]); //=> [-1, -5, 2, 10]<br />// 返回无重复元素的列表。元素通过 predicate 进行相同性判断。如果通过 predicate 判断两元素相同,保留第一个元素。const strEq = R.eqBy(String);R.uniqWith(strEq)([1, '1', 2, 1]); //=> [1, 2]R.uniqWith(strEq)([{}, {}]); //=> [{}]R.uniqWith(strEq)([1, '1', 1]); //=> [1]R.uniqWith(strEq)(['1', 1, 1]); //=> ['1']<br />// 集合并运算,合并两个列表为新列表(新列表中无重复元素)。R.union([1, 2, 3], [2, 3, 4]); //=> [1, 2, 3, 4]<br />// 集合并运算,合并两个列表为新列表(新列表中无重复元素)。由 predicate 的返回值决定两元素是否重复。const l1 = [{a: 1}, {a: 2}];const l2 = [{a: 1}, {a: 4}];R.unionWith(R.eqBy(R.prop('a')), l1, l2); //=> [{a: 1}, {a: 2}, {a: 4}]<br />// 返回一个没有连续重复元素的 list。首个参数提供的 predicate 用于检测 list 中相邻的两个元素是否相等。一系列相等元素中的首个元素会被保留。const l = [1, -1, 1, 3, 4, -4, -4, -5, 5, 3, 3];R.dropRepeatsWith(R.eqBy(Math.abs), l); //=> [1, 3, 4, -5, 3]
find,findIndex,findLast,findLastIndex// 查找并返回 list 中首个满足 predicate 的元素;如果未找到满足条件的元素,则返回 undefinedconst xs = [{a: 1}, {a: 2}, {a: 3}];R.find(R.propEq('a', 2))(xs); //=> {a: 2}R.find(R.propEq('a', 4))(xs); //=> undefined<br />// 查找并返回 list 中首个满足 predicate 的元素的索引;如果未找到满足条件的元素,则返回 -1 。const xs = [{a: 1}, {a: 2}, {a: 3}];R.findIndex(R.propEq('a', 2))(xs); //=> 1R.findIndex(R.propEq('a', 4))(xs); //=> -1<br />// 查找并返回 list 中最后一个满足 predicate 的元素;如果未找到满足条件的元素,则返回 undefined 。const xs = [{a: 1, b: 0}, {a:1, b: 1}];R.findLast(R.propEq('a', 1))(xs); //=> {a: 1, b: 1}R.findLast(R.propEq('a', 4))(xs); //=> undefined<br />// 查找并返回 list 中最后一个满足 predicate 的元素的索引;如果未找到满足条件的元素,则返回 -1 。const xs = [{a: 1, b: 0}, {a:1, b: 1}];R.findLastIndex(R.propEq('a', 1))(xs); //=> 1R.findLastIndex(R.propEq('a', 4))(xs); //=> -1
chain,flatten,unnest// chain 将函数映射到列表中每个元素,并将结果连接起来。 chain 在一些库中也称为 flatMap(先 map 再 flatten )。const duplicate = n => [n, n];R.chain(duplicate, [1, 2, 3]); //=> [1, 1, 2, 2, 3, 3]R.chain(R.append, R.head)([1, 2, 3]); //=> [1, 2, 3, 1]<br />// 获取 list 的所有元素(包含所有子数组中的元素),然后由这些元素组成一个新的数组。深度优先。R.flatten([1, 2, [3, 4], 5, [6, [7, 8, [9, [10, 11], 12]]]]);//=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]<br />// R.chain(R.identity) 的简写, 对 [Chain](https://github.com/fantasyland/fantasy-land#chain) 类型的数据消除一层嵌套.R.unnest([1, [2], [[3]]]); //=> [1, 2, [3]]R.unnest([[1, 2], [3, 4], [5, 6]]); //=> [1, 2, 3, 4, 5, 6]
indexOf,lastIndexOf// 返回给定元素在数组中首次出现时的索引值,如果数组中没有该元素,则返回 -1。通过 [R.equals](https://ramda.cn/docs/#equals) 函数进行相等性判断。R.indexOf(3, [1,2,3,4]); //=> 2R.indexOf(10, [1,2,3,4]); //=> -1<br />// 返回数组中某元素最后一次出现的位置,如果数组中不包含该项则返回 -1 。通过 [R.equals](https://ramda.cn/docs/#equals) 函数进行相等性判断R.lastIndexOf(3, [-1,3,3,0,1,2,3,4]); //=> 6R.lastIndexOf(10, [1,2,3,4]); //=> -1
join,split, splitAt// 将列表中所有元素通过 分隔符 串连为一个字符串。const spacer = R.join(' ');spacer(['a', 2, 3.4]); //=> 'a 2 3.4'R.join('|', [1, 2, 3]); //=> '1|2|3'<br />// 根据指定的分隔符将字符串拆分为字符串类型的数组。const pathComponents = R.split('/');R.tail(pathComponents('/usr/local/bin/node')); //=> ['usr', 'local', 'bin', 'node']R.split('.', 'a.b.c.xyz.d'); //=> ['a', 'b', 'c', 'xyz', 'd']<br />// 在指定的索引处拆分列表或者字符串。R.splitAt(1, [1, 2, 3]); //=> [[1], [2, 3]]R.splitAt(5, 'hello world'); //=> ['hello', ' world']R.splitAt(-1, 'foobar'); //=> ['fooba', 'r']
nth,nthArgconst list = ['foo', 'bar', 'baz', 'quux'];R.nth(1, list); //=> 'bar'R.nth(-1, list); //=> 'quux'R.nth(-99, list); //=> undefined<br />R.nth(2, 'abc'); //=> 'c'R.nth(3, 'abc'); //=> ''<br />R.nthArg(1)('a', 'b', 'c'); //=> 'b'R.nthArg(-1)('a', 'b', 'c'); //=> 'c'
head,last,init,tail// 求列表或字符串的首个元素。在某些库中,该函数也被称作 first。R.head(['fi', 'fo', 'fum']); //=> 'fi'R.head([]); //=> undefined<br />R.head('abc'); //=> 'a'R.head(''); //=> ''<br />R.last(['fi', 'fo', 'fum']); //=> 'fum'R.last([]); //=> undefined<br />R.last('abc'); //=> 'c'R.last(''); //=> ''<br />//返回 list 或 string 删除最后一个元素后的部分。R.init([1, 2, 3]); //=> [1, 2]R.init([1, 2]); //=> [1]R.init([1]); //=> []R.init([]); //=> []<br />//删除列表中的首个元素(或者调用对象的 tail 方法)R.tail([1, 2, 3]); //=> [2, 3]R.tail([1, 2]); //=> [2]R.tail([1]); //=> []R.tail([]); //=> []
transpose, xprod,zip, zipObj, zipWith// 二维数组行列转置。输入 n 个长度为 x 的数组,输出 x 个长度为 n 的数组。R.transpose([[1, 'a'], [2, 'b'], [3, 'c']]) //=> [[1, 2, 3], ['a', 'b', 'c']]R.transpose([[1, 2, 3], ['a', 'b', 'c']]) //=> [[1, 'a'], [2, 'b'], [3, 'c']]<br />// 将两个列表的元素两两组合,生成一个新的元素对列表。R.xprod([1, 2], ['a', 'b']); //=> [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]<br />// 将两个列表对应位置的元素组合,生成一个新的元素对列表。生成的列表长度取决于较短的输入列表的长度。R.zip([1, 2, 3], ['a', 'b', 'c']); //=> [[1, 'a'], [2, 'b'], [3, 'c']]<br />// 将两个列表对应位置的元素作为键值对组合,生成一个新的键值对的列表。生成的列表长度取决于较短的输入列表的长度。R.zipObj(['a', 'b', 'c'], [1, 2, 3]); //=> {a: 1, b: 2, c: 3}<br />// 将两个列表对应位置的元素通过一个函数处理,生成一个新的元素的列表。生成的列表长度取决于较短的输入列表的长度。const f = (x, y) => {// ...};R.zipWith(f, [1, 2, 3], ['a', 'b', 'c']);//=> [f(1, 'a'), f(2, 'b'), f(3, 'c')]
- 高阶函数
o,compose,composeWith,pipe,pipeWith// 类似于 [compose](https://ramda.cn/docs/#compose),o 从右到左执行函数组合。但与 [compose](https://ramda.cn/docs/#compose) 不同的是,传递给 o 的最右边的函数为一元函数。const classyGreeting = name => "The name's " + name.last + ", " + name.first + " " + name.lastconst yellGreeting = R.o(R.toUpper, classyGreeting);yellGreeting({first: 'James', last: 'Bond'}); //=> "THE NAME'S BOND, JAMES BOND"<br />R.o(R.multiply(10), R.add(10))(-4) //=> 60<br />//从右往左执行函数组合(右侧函数的输出作为左侧函数的输入)。最后一个函数可以是任意元函数(参数个数不限),其余函数必须是一元函数。const classyGreeting = (firstName, lastName) => "The name's " + lastName + ", " + firstName + " " + lastNameconst yellGreeting = R.compose(R.toUpper, classyGreeting);yellGreeting('James', 'Bond'); //=> "THE NAME'S BOND, JAMES BOND"<br />R.compose(Math.abs, R.add(1), R.multiply(2))(-4) //=> 7<br />// 利用转换函数从右往左执行函数组合。最后一个函数可以是任意元函数(参数个数不限),其余函数必须是一元函数。const composeWhileNotNil = R.composeWith((f, res) => R.isNil(res) ? res : f(res));composeWhileNotNil([R.inc, R.prop('age')])({age: 1}) //=> 2composeWhileNotNil([R.inc, R.prop('age')])({}) //=> undefined<br />// 从左往右执行函数组合。第一个函数可以是任意元函数(参数个数不限),其余函数必须是一元函数。const f = R.pipe(Math.pow, R.negate, R.inc);f(3, 4); // -(3^4) + 1<br />// 利用转换函数从左往右执行函数组合。第一个函数可以是任意元函数(参数个数不限),其余函数必须是一元函数。const pipeWhileNotNil = R.pipeWith((f, res) => R.isNil(res) ? res : f(res));const f = pipeWhileNotNil([Math.pow, R.negate, R.inc])f(3, 4); // -(3^4) + 1
- 排序函数
ascend, descend,sort, sortBy,sortWithconst byAge = R.ascend(R.prop('age'));const people = [{ name: 'Emma', age: 70 },{ name: 'Peter', age: 78 },{ name: 'Mikhail', age: 62 },];const peopleByYoungestFirst = R.sort(byAge, people);//=> [{ name: 'Mikhail', age: 62 },{ name: 'Emma', age: 70 }, { name: 'Peter', age: 78 }]<br />const byAge = R.descend(R.prop('age'));const people = [{ name: 'Emma', age: 70 },{ name: 'Peter', age: 78 },{ name: 'Mikhail', age: 62 },];const peopleByOldestFirst = R.sort(byAge, people);//=> [{ name: 'Peter', age: 78 }, { name: 'Emma', age: 70 }, { name: 'Mikhail', age: 62 }]<br />// 使用比较函数对列表进行排序。比较函数每次接受两个参数,如果第一个值较小,则返回负数;如果第一个值较大,则返回正数;如果两值相等,返回零。注意,返回的是列表的 **拷贝 **,不会修改原列表。const diff = function(a, b) { return a - b; };R.sort(diff, [4,2,7,5]); //=> [2, 4, 5, 7]<br />//根据给定的函数对列表进行排序。const sortByFirstItem = R.sortBy(R.prop(0));const pairs = [[-1, 1], [-2, 2], [-3, 3]];sortByFirstItem(pairs); //=> [[-3, 3], [-2, 2], [-1, 1]]<br />//依据比较函数列表对输入列表进行排序。const alice = {name: 'alice',age: 40};const bob = {name: 'bob',age: 30};const clara = {name: 'clara',age: 40};const people = [clara, bob, alice];const ageNameSort = R.sortWith([R.descend(R.prop('age')),R.ascend(R.prop('name'))]);ageNameSort(people); //=> [alice, clara, bob]
comparatorconst byAge = R.comparator((a, b) => a.age < b.age);const people = [{ name: 'Emma', age: 70 },{ name: 'Peter', age: 78 },{ name: 'Mikhail', age: 62 },];const peopleByIncreasingAge = R.sort(byAge, people);//=> [{ name: 'Mikhail', age: 62 },{ name: 'Emma', age: 70 }, { name: 'Peter', age: 78 }]<br /><br />
- 辅助函数,处理参数
apply, call, unapply// 将函数 fn 作用于参数列表 args。apply 可以将变参函数转换为为定参函数。如果上下文很重要,则 fn 应该绑定其上下文。const nums = [1, 2, 3, -99, 42, 6, 7];R.apply(Math.max, nums); //=> 42<br />R.call(R.add, 1, 2); //=> 3//R.call 可以用作 [R.converge](https://ramda.cn/docs/#converge) 的 convergeing 函数:第一个分支函数生成函数,其余分支函数生成一系列值作为该函数的参数。(R.converge 第二个参数为一个分支函数列表const indentN = R.pipe(R.repeat(' '),R.join(''),R.replace(/^(?!$)/gm));const format = R.converge(R.call, [R.pipe(R.prop('indent'), indentN),R.prop('value')]);format({indent: 2, value: 'foo\nbar\nbaz\n'}); //=> ' foo\n bar\n baz\n'<br />// 换言之,R.unapply 将一个使用数组作为参数的函数,变为一个不定参函数。 R.unapply 是 [R.apply](https://ramda.cn/docs/#apply) 的逆函R.unapply(JSON.stringify)(1, 2, 3); //=> '[1,2,3]'
binary,nAry,unaryconst takesTwoArgs = R.binary(takesThreeArgs);takesTwoArgs.length; //=> 2takesTwoArgs(1, 2, 3); //=> [1, 2, undefined]<br />// 将一个任意元(包括零元)的函数,封装成一个确定元数(参数个数)的函数。任何多余的参数都不会传入被封装的函数。const takesOneArg = R.nAry(1, takesTwoArgs);takesOneArg.length; //=> 1takesOneArg(1, 2); //=> [1, undefined]<br />// 将任意元(包括零元)函数封装成一元函数。任何额外的参数都不会传递给被封装的函数。const takesOneArg = R.unary(takesTwoArgs);takesOneArg.length; //=> 1takesOneArg(1, 2); //=> [1, undefined]
partial, partialRight,curry, curryN, uncurryNconst multiply2 = (a, b) => a * b;const double = R.partial(multiply2, [2]);double(2); //=> 4<br />const greet = (salutation, title, firstName, lastName) =>salutation + ', ' + title + ' ' + firstName + ' ' + lastName + '!';const sayHello = R.partial(greet, ['Hello']);const sayHelloToMs = R.partial(sayHello, ['Ms.']);sayHelloToMs('Jane', 'Jones'); //=> 'Hello, Ms. Jane Jones!'<br />const greet = (salutation, title, firstName, lastName) =>salutation + ', ' + title + ' ' + firstName + ' ' + lastName + '!';const greetMsJaneJones = R.partialRight(greet, ['Ms.', 'Jane', 'Jones']);greetMsJaneJones('Hello'); //=> 'Hello, Ms. Jane Jones!'<br />const addFourNumbers = (a, b, c, d) => a + b + c + d;const curriedAddFourNumbers = R.curry(addFourNumbers);const f = curriedAddFourNumbers(1, 2);const g = f(3);g(4); //=> 10<br />const sumArgs = (...args) => R.sum(args);const curriedAddFourNumbers = R.curryN(4, sumArgs);const f = curriedAddFourNumbers(1, 2);const g = f(3);g(4); //=> 10<br />// 将一个柯里化的函数转换为一个 n 元函数。const addFour = a => b => c => d => a + b + c + d;const uncurriedAddFour = R.uncurryN(4, addFour);uncurriedAddFour(1, 2, 3, 4); //=> 10
- 其他
clampR.clamp(1, 10, -5) // => 1R.clamp(1, 10, 15) // => 10R.clamp(1, 10, 4) // => 4
clone// 深复制。其值可能(嵌套)包含 Array、Object、Number、String、Boolean、Date 类型的数据。Function 通过引用复制。const objects = [{}, {}, {}];const objectsClone = R.clone(objects);objects === objectsClone; //=> falseobjects[0] === objectsClone[0]; //=> false
empty// 根据传入参数的类型返回其对应的空值。Ramda 定义了各类型的空值如下:Array ([]),Object ({}),String (''),和 ArgumentsR.empty(Just(42)); //=> Nothing()R.empty([1, 2, 3]); //=> []R.empty('unicorns'); //=> ''R.empty({x: 1, y: 2}); //=> {}
flip// 交换函数前两个参数的位置。const mergeThree = (a, b, c) => [].concat(a, b, c);mergeThree(1, 2, 3); //=> [1, 2, 3]R.flip(mergeThree)(1, 2, 3); //=> [2, 1, 3]
tap// 对输入的值执行给定的函数,然后返回输入的值。const sayX = x => console.log('x is ' + x);R.tap(sayX, 100); //=> 100// logs 'x is 100'
type// 用一个单词来描述输入值的(原生)类型,返回诸如 'Object'、'Number'、'Array'、'Null' 之类的结果。不区分用户自定义的类型,统一返回 'Object'。R.type({}); //=> "Object"R.type(1); //=> "Number"R.type(false); //=> "Boolean"R.type('s'); //=> "String"R.type(null); //=> "Null"R.type([]); //=> "Array"R.type(/[A-z]/); //=> "RegExp"R.type(() => {}); //=> "Function"R.type(undefined); //=> "Undefined"
keys,keysIn,values, valuesIn// 返回给定对象所有可枚举的、自身属性的属性名组成的列表。注意,不同 JS 运行环境输出数组的顺序可能不一致。R.keys({a: 1, b: 2, c: 3}); //=> ['a', 'b', 'c']<br />// 返回给定对象所有属性(包括 prototype 属性)的属性名组成的列表。注意,不同 JS 运行环境输出数组的顺序可能不一致。const F = function() { this.x = 'X'; };F.prototype.y = 'Y';const f = new F();R.keysIn(f); //=> ['x', 'y']<br />// 返回对象所有自身可枚举的属性的值。注意:不同 JS 运行环境输出数组的顺序可能不一致。R.values({a: 1, b: 2, c: 3}); //=> [1, 2, 3]<br />// 返回对象所有属性的值,包括原型链上的属性。注意:不同 JS 运行环境输出数组的顺序可能不一致。const F = function() { this.x = 'X'; };F.prototype.y = 'Y';const f = new F();R.valuesIn(f); //=> ['X', 'Y']
memoizeWith,once// 创建一个新函数,当调用时,会执行原函数,输出结果;并且缓存本次的输入参数及其对应的结果。 后续,若用相同的参数对缓存函数进行调用,不会再执行原函数,而是直接返回该参数对应的缓存值。let count = 0;const factorial = R.memoizeWith(R.identity, n => {count += 1;return R.product(R.range(1, n + 1));});factorial(5); //=> 120factorial(5); //=> 120factorial(5); //=> 120count; //=> 1<br />// 创建一个只执行一次的函数。将给定函数 fn 封装到新函数fn'中,fn' 确保 fn 只能调用一次。重复调用fn' ,只会返回第一次执行时的结果。const addOneOnce = R.once(x => x + 1);addOneOnce(10); //=> 11addOneOnce(addOneOnce(50)); //=> 11
test,match// 检测字符串是否匹配给定的正则表达式。R.test(/^x/, 'xyz'); //=> trueR.test(/^y/, 'xyz'); //=> false<br />// 正则匹配字符串。注意,如果没有匹配项,则返回空数组。和 [String.prototype.match](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match) 不同,后者在没有匹配项时会返回 null。R.match(/([a-z]a)/g, 'bananas'); //=> ['ba', 'na', 'na']R.match(/a/, 'b'); //=> []R.match(/a/, null); //=> TypeError: null does not have a method named "match"
range// 返回从 from 到 to 之间的所有数的升序列表。左闭右开(包含 from,不包含 to)。R.range(1, 5); //=> [1, 2, 3, 4]R.range(50, 53); //=> [50, 51, 52]
repeat,times// 生成包含 n 个同一元素的数组。R.repeat('hi', 5); //=> ['hi', 'hi', 'hi', 'hi', 'hi']const obj = {};const repeatedObjs = R.repeat(obj, 5); //=> [{}, {}, {}, {}, {}]repeatedObjs[0] === repeatedObjs[1]; //=> true<br />//执行输入的函数 n 次,返回由函数执行结果组成的数组。fn 为一元函数,n 次调用接收的参数为:从 0 递增到 n-1 。R.times(R.identity, 5); //=> [0, 1, 2, 3, 4]
toLower, toUpper// 将字符串转换成小写。R.toLower('XYZ'); //=> 'xyz'<br />// 将字符串转换为大写。R.toUpper('abc'); //=> 'ABC'
tryCatch// tryCatch 接受两个函数:tryer 和 catcher,生成的函数执行 tryer,若未抛出异常,则返回执行结果。若抛出异常,则执行 catcher,返回 catcher 的执行结果R.tryCatch(R.prop('x'), R.F)({x: true}); //=> trueR.tryCatch(() => { throw 'foo'}, R.always('catched'))('bar') // => 'catched'R.tryCatch(R.times(R.identity), R.always([]))('s') // => []R.tryCatch(() => { throw 'this is not a valid value'}, (err, value)=>({error : err, value }))('bar') // => {'error': 'this is not a valid value', 'value': 'bar'}
until// 接受一个 predicate ,transform function 和 初始值,返回一个与初始值相同类型的值。对输入值进行 transform ,直到 transform 的结果满足 predicate,此时返回这个满足 predicate 的值。R.until(R.gt(R.__, 100), R.multiply(2))(1) // => 128
相关文档:
Ramda.js与函数式编程
ramda使用整理.md
暂时未理解到的用法:
andThen ,otherwise,Bind,Both,either,Composek,pipeK,Composep,pipeP,construct,invoker,constructN,Thunkify,sequence,traverse,useWith?
