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) // 3
curryMathAbs()(-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.__表示占位符,源码__.js
module.exports = {
'@@functional/placeholder': true
};
<br />
// 判断是否是占位符,源码_isPlaceholder.js
function _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) // 3
if (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岁了, 我喜欢瞎bb
msg('小明')(_, _)(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); //=> 5
R.add(7)(10); //=> 17
<br />
// 自动加1
R.inc(42); //=> 43
<br />
// 减
R.subtract(10, 8); //=> 2
const minus5 = R.subtract(R.__, 5);
minus5(17); //=> 12
<br />
// 自动减1
R.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); //=> 789
R.max('a', 'b'); //=> 'b'
<br />
//包裹函数判断最大值
const square = n => n * n;
R.maxBy(square, -3, 2); //=> -3
R.reduce(R.maxBy(square), 0, [3, -5, 4, 1, -2]); //=> -5
<br />
// 算最小值
R.min(789, 123); //=> 123
R.min('a', 'b'); //=> 'a'
<br />
//包裹函数判断最小值
const square = n => n * n;
R.minBy(square, -3, 2); //=> 2
R.reduce(R.minBy(square), Infinity, [3, -5, 4, 1, -2]); //=> 1
<br />
// 返回给定数字列表的平均值。
R.mean([2, 7, 9]); //=> 6
R.mean([]); //=> NaN
<br />
// 返回给定数字列表的中位数。
R.median([2, 9, 7]); //=> 7
R.median([7, 2, 10, 9]); //=> 8
R.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); //=> 3
R.mathMod(17, 5); //=> 2
R.mathMod(17, -5); //=> NaN
R.mathMod(17, 0); //=> NaN
R.mathMod(17.2, 5); //=> NaN
R.mathMod(17, 5.3); //=> NaN
<br />
//用第一个参数除以第二个参数,并返回余数。注意,该函数是 JavaScript-style 的求模操作。数学求模另见 mathMod。
R.modulo(17, 3); //=> 2
// JS behavior:
R.modulo(-17, 3); //=> -2
R.modulo(17, -3); //=> 2
const isOdd = R.modulo(R.__, 2);
isOdd(42); //=> 0
isOdd(21); //=> 1
- 判断
all, any,none,contains,includes,has,hasIn,hasPath,identical,is,isEmpty,isNil,where, whereEq// 如果列表中的所有元素都满足 predicate,则返回 true;否则,返回 false
const 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]); //=> true
R.contains(4, [1, 2, 3]); //=> false
R.contains({ name: 'Fred' }, [{ name: 'Fred' }]); //=> true
R.contains([42], [[42]]); //=> true
R.contains('ba', 'banana'); //=>true
<br />
// 只要列表中有一个元素等于指定值,则返回 true;否则返回 false。通过 R.equals 函数进行相等性判断。
R.includes(3, [1, 2, 3]); //=> true
R.includes(4, [1, 2, 3]); //=> false
R.includes({ name: 'Fred' }, [{ name: 'Fred' }]); //=> true
R.includes([42], [[42]]); //=> true
R.includes('ba', 'banana'); //=>true
<br />
// 如果对象自身含有指定的属性,则返回 true;否则返回 false。
const hasName = R.has('name');
hasName({name: 'alice'}); //=> true
hasName({name: 'bob'}); //=> true
hasName({}); //=> 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); //=> true
R.hasIn('area', square); //=> true
<br />
// 检查对象中是否存在指定的路径。只检查对象自身的属性。
R.hasPath(['a', 'b'], {a: {b: 2}}); // => true
R.hasPath(['a', 'b'], {a: {b: undefined}}); // => true
R.hasPath(['a', 'b'], {a: {c: 2}}); // => false
R.hasPath(['a', 'b'], {}); // => false
<br />
// 如果两个参数是完全相同,则返回 true,否则返回 false。如果它们引用相同的内存,也认为是完全相同的。NaN 和 NaN 是完全相同的;0 和 -0 不是完全相同的。
const o = {};
R.identical(o, o); //=> true
R.identical(1, 1); //=> true
R.identical(1, '1'); //=> false
R.identical([], []); //=> false
R.identical(0, -0); //=> false
R.identical(NaN, NaN); //=> true
<br />
// 检测一个对象(val)是否是给定构造函数的实例。该函数会依次检测其原型链,如果存在的话。
R.is(Object, {}); //=> true
R.is(Number, 1); //=> true
R.is(Object, 1); //=> false
R.is(String, 's'); //=> true
R.is(String, new String('')); //=> true
R.is(Object, new String('')); //=> true
R.is(Object, 's'); //=> false
R.is(Number, {}); //=> false
<br />
//检测给定值是否为其所属类型的空值,若是则返回 true ;否则返回 false 。
R.isEmpty([1, 2, 3]); //=> false
R.isEmpty([]); //=> true
R.isEmpty(''); //=> true
R.isEmpty(null); //=> false
R.isEmpty({}); //=> true
R.isEmpty({length: 0}); //=> false
<br />
// 检测输入值是否为 null 或 undefined 。
R.isNil(null); //=> true
R.isNil(undefined); //=> true
R.isNil(0); //=> false
R.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}); //=> true
pred({a: 'xxx', b: 'xxx', x: 11, y: 19}); //=> false
pred({a: 'foo', b: 'bar', x: 11, y: 19}); //=> false
pred({a: 'foo', b: 'xxx', x: 10, y: 19}); //=> false
pred({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}); //=> false
pred({a: 1, b: 2}); //=> true
pred({a: 1, b: 2, c: 3}); //=> true
pred({a: 1, b: 1}); //=> false
allPass, anyPass// 传入包含多个 predicate 的列表,返回一个 predicate:如果给定的参数满足列表中的所有 predicate ,则返回 true
const isQueen = R.propEq('rank', 'Q');
const isSpade = R.propEq('suit', '♠︎');
const isQueenOfSpades = R.allPass([isQueen, isSpade]);
isQueenOfSpades({rank: 'Q', suit: '♣︎'}); //=> false
isQueenOfSpades({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: '♣'}); //=> true
isBlackCard({rank: 'Q', suit: '♦'}); //=> false
endsWith, starsWith,eqBy, eqProps,equals// 检查列表是否以指定的子列表结尾
R.endsWith('c', 'abc') //=> true
R.endsWith('b', 'abc') //=> false
R.endsWith(['c'], ['a', 'b', 'c']) //=> true
R.endsWith(['b'], ['a', 'b', 'c']) //=> false
<br />
// 检查列表是否以给定的值开头。
R.startsWith('a', 'abc') //=> true
R.startsWith('b', 'abc') //=> false
R.startsWith(['a'], ['a', 'b', 'c']) //=> true
R.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); //=> false
R.eqProps('c', o1, o2); //=> true
<br />
// 如果传入的参数相等,返回 true;否则返回 false。可以处理几乎所有 JavaScript 支持的数据结构。
R.equals(1, 1); //=> true
R.equals(1, '1'); //=> false
R.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); //=> null
safeInc(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); //=> true
R.gt(2, 2); //=> false
<br />
R.lt(2, 1); //=> false
R.lt(2, 2); //=> false
<br />
R.gte(2, 1); //=> true
R.gte(2, 2); //=> true
<br />
R.lte(2, 1); //=> false
R.lte(2, 2); //=> true
- 逻辑方法
//如果两个参数都是 true,则返回 true;否则返回 false, 相当于&&
R.and(true, true); //=> true
R.and(true, false); //=> false
R.and(false, true); //=> false
R.and(false, false); //=> false
<br />
//如果其中一个参数为真,另一个参数为假,则返回true ;否则返回false,异或操作
R.xor(true, true); //=> false
R.xor(true, false); //=> true
R.xor(false, true); //=> true
R.xor(false, false); //=> false
<br />
//只要有一个参数为真(truth-y),就返回 true;否则返回 false,相当于||
R.or(true, true); //=> true
R.or(true, false); //=> true
R.or(false, true); //=> true
R.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); //=> true
f(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); //=> true
f(8); //=> true
<br />
// 对函数的返回值取反。接受一个函数 f,返回一个新函数 g:在输入参数相同的情况下,若 f 返回 'true-y' ,则 g 返回 false-y ,反之亦然
const isNotNil = R.complement(R.isNil);
R.isNil(null); //=> true
isNotNil(null); //=> false
R.isNil(7); //=> false
isNotNil(7); //=> true
<br />
R.not(true); //=> false
R.not(false); //=> true
R.not(0); //=> true
R.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) : r
R.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) : r
R.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); //=> 81
R.useWith(Math.pow, [R.identity, R.identity])(3)(4); //=> 81
R.useWith(Math.pow, [R.dec, R.inc])(3, 4); //=> 32
R.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}); //=> 1
R.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}); //=> 1
R.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}]});
//=> 2
R.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}); //=> 1
R.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}); //=> 100
R.prop('x', {}); //=> undefined
R.prop(0, [100]); //=> 100
R.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); //=> undefined
favoriteWithDefault(alice); //=> 'Ramda'
<br />
// 判断指定对象的属性是否为给定的数据类型,是则返回 true ;否则返回 false 。
R.propIs(Number, 'x', {x: 1, y: 2}); //=> true
R.propIs(Number, 'x', {x: 'foo'}); //=> false
R.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}}); //=> 2
R.path(['a', 'b'], {c: {b: 2}}); //=> undefined
R.path(['a', 'b', 0], {a: {b: [1, 2, 3]}}); //=> 1
R.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}}); //=> 2
R.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}}); //=> true
R.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 的元素;如果未找到满足条件的元素,则返回 undefined
const 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); //=> 1
R.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); //=> 1
R.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]); //=> 2
R.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]); //=> 6
R.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.last
const 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 + " " + lastName
const 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}) //=> 2
composeWhileNotNil([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; //=> 2
takesTwoArgs(1, 2, 3); //=> [1, 2, undefined]
<br />
// 将一个任意元(包括零元)的函数,封装成一个确定元数(参数个数)的函数。任何多余的参数都不会传入被封装的函数。
const takesOneArg = R.nAry(1, takesTwoArgs);
takesOneArg.length; //=> 1
takesOneArg(1, 2); //=> [1, undefined]
<br />
// 将任意元(包括零元)函数封装成一元函数。任何额外的参数都不会传递给被封装的函数。
const takesOneArg = R.unary(takesTwoArgs);
takesOneArg.length; //=> 1
takesOneArg(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) // => 1
R.clamp(1, 10, 15) // => 10
R.clamp(1, 10, 4) // => 4
clone// 深复制。其值可能(嵌套)包含 Array、Object、Number、String、Boolean、Date 类型的数据。Function 通过引用复制。
const objects = [{}, {}, {}];
const objectsClone = R.clone(objects);
objects === objectsClone; //=> false
objects[0] === objectsClone[0]; //=> false
empty// 根据传入参数的类型返回其对应的空值。Ramda 定义了各类型的空值如下:Array ([]),Object ({}),String (''),和 Arguments
R.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); //=> 120
factorial(5); //=> 120
factorial(5); //=> 120
count; //=> 1
<br />
// 创建一个只执行一次的函数。将给定函数 fn 封装到新函数fn'中,fn' 确保 fn 只能调用一次。重复调用fn' ,只会返回第一次执行时的结果。
const addOneOnce = R.once(x => x + 1);
addOneOnce(10); //=> 11
addOneOnce(addOneOnce(50)); //=> 11
test,match// 检测字符串是否匹配给定的正则表达式。
R.test(/^x/, 'xyz'); //=> true
R.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}); //=> true
R.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?