学习函数式编程的过程中,我接触到了 Ramda.js

我发现,这是一个很重要的库,提供了许多有用的方法,每个 JavaScript 程序员都应该掌握这个工具。

你可能会问,Underscore 和 Lodash 已经这么流行了,为什么还要学习好像雷同的 Ramda 呢?

回答是,前两者的参数位置不对,把处理的数据放到了第一个参数。


var square = n => n * n;
_.map([4, 8], square) // [16, 64]

上面代码中,_.map的第一个参数[4, 8]是要处理的数据,第二个参数square是数据要执行的运算。

Ramda 的数据一律放在最后一个参数,理念是"function first,data last"。


var R = require('ramda');
R.map(square, [4, 8]) // [16, 64]

为什么 Underscore 和 Lodash 是错的,而 Ramda 是对的?这放在下一篇文章详细解释,今天我主要介绍 Ramda 提供的几十个方法。这是理解以后的内容所必须的。

除了数据放在最后一个参数,Ramda 还有一个特点:所有方法都支持柯里化。

也就是说,所有多参数的函数,默认都可以单参数使用。


// 写法一
R.map(square, [4, 8]) // 写法二
R.map(square)([4, 8])
// 或者
var mapSquare = R.map(square);
mapSquare([4, 8]);

上面代码中,写法一是多参数版本,写法二是柯里化以后的单参数版本。Ramda 都支持,并且推荐使用第二种写法。

由于这两个特点,使得 Ramda 成为 JavaScript 函数式编程最理想的工具库。今天,我先介绍它的 API,下一次再介绍这些方法如何用于实战。我保证,一旦你理解了它的运算模型,就一定会认同这才是正确的计算方法。

下面所有的示例,都可以在线上测试环境运行。

Ramda API 介绍

目录

一、比较运算 
二、数学运算 
三、逻辑运算 
四、字符串 
五、函数 
- 5.1 函数的合成 
- 5.2 柯里化 
- 5.3 函数的执行 
六、数组 
- 6.1 数组的特征判断 
- 6.2 数组的截取和添加 
- 6.3 数组的过滤 
- 6.4 单数组运算 
- 6.5 双数组运算 
- 6.6 复合数组 
七、对象 
- 7.1 对象的特征判断 
- 7.2 对象的过滤 
- 7.3 对象的截取 
- 7.4 对象的运算 
- 7.5 复合对象

一、比较运算

gt:判断第一个参数是否大于第二个参数。


R.gt(2)(1) // true
R.gt('a')('z') // false

gte:判断第一个参数是否大于等于第二个参数。


R.gte(2)(2) // true
R.gte('a')('z') // false

lt:判断第一个参数是否小于第二个参数。


R.lt(2)(1) // false
R.lt('a')('z') // true

lte:判断第一个参数是否小于等于第二个参数。


R.lte(2)(2) // true
R.lte('a')('z') // true

equals:比较两个值是否相等(支持对象的比较)。


R.equals(1)(1) // true
R.equals(1)('1') // false
R.equals([1, 2, 3])([1, 2, 3]) // true var a = {};
a.v = a;
var b = {};
b.v = b;
R.equals(a)(b)
// true

eqBy:比较两个值传入指定函数的运算结果是否相等。


R.eqBy(Math.abs, 5)(-5)
// true

二、数学运算

add:返回两个值的和。


R.add(7)(10) // 17

subtract:返回第一个参数减第二个参数的差。


R.subtract(10)(8) // 2

multiply:返回两个值的积。


R.multiply(2)(5) // 10

divide:返回第一个参数除以第二个参数的商。


R.divide(71)(100) // 0.71

三、逻辑运算

either:接受两个函数作为参数,只要有一个返回true,就返回true,否则返回false。相当于||运算。


var gt10 = x => x > 10;
var even = x => x % 2 === 0; var f = R.either(gt10, even);
f(101) // true
f(8) // true

both:接受两个函数作为参数,只有它们都返回true,才返回true,否则返回false,相当于&&运算。


var gt10 = x => x > 10;
var even = x => x % 2 === 0; var f = R.both(gt10, even);
f(15) // false
f(30) // true

allPass:接受一个函数数组作为参数,只有它们都返回true,才返回true,否则返回false


var gt10 = x => x > 10;
var even = x => x % 2 === 0; var isEvenAndGt10 = R.allPass([gt10, even]);
isEvenAndGt10(15) // false
isEvenAndGt10(30) // true

四、字符串

split:按照指定分隔符将字符串拆成一个数组。


R.split('.')('a.b.c.xyz.d')
// ['a', 'b', 'c', 'xyz', 'd']

test:判断一个字符串是否匹配给定的正则表达式。


R.test(/^x/)('xyz')
// true R.test(/^y/)('xyz')
// false

match:返回一个字符串的匹配结果。


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"

五、函数

5.1 函数的合成

compose:将多个函数合并成一个函数,从右到左执行。


R.compose(Math.abs, R.add(1), R.multiply(2))(-4) // 7

pipe:将多个函数合并成一个函数,从左到右执行。


var negative = x => -1 * x;
var increaseOne = x => x + 1; var f = R.pipe(Math.pow, negative, increaseOne);
f(3, 4) // -80 => -(3^4) + 1

converge:接受两个参数,第一个参数是函数,第二个参数是函数数组。传入的值先使用第二个参数包含的函数分别处理以后,再用第一个参数处理前一步生成的结果。


var sumOfArr = arr => {
var sum = 0;
arr.forEach(i => sum += i);
return sum;
};
var lengthOfArr = arr => arr.length; var average = R.converge(R.divide, [sumOfArr, lengthOfArr])
average([1, 2, 3, 4, 5, 6, 7])
// 4
// 相当于 28 除以 7 var toUpperCase = s => s.toUpperCase();
var toLowerCase = s => s.toLowerCase();
var strangeConcat = R.converge(R.concat, [toUpperCase, toLowerCase])
strangeConcat("Yodel")
// "YODELyodel"
// 相当于 R.concat('YODEL', 'yodel')

5.2 柯里化

curry:将多参数的函数,转换成单参数的形式。


var addFourNumbers = (a, b, c, d) => a + b + c + d; var curriedAddFourNumbers = R.curry(addFourNumbers);
var f = curriedAddFourNumbers(1, 2);
var g = f(3);
g(4) // 10

partial:允许多参数的函数接受一个数组,指定最左边的部分参数。


var multiply2 = (a, b) => a * b;
var double = R.partial(multiply2, [2]);
double(2) // 4 var greet = (salutation, title, firstName, lastName) =>
salutation + ', ' + title + ' ' + firstName + ' ' + lastName + '!'; var sayHello = R.partial(greet, ['Hello']);
var sayHelloToMs = R.partial(sayHello, ['Ms.']);
sayHelloToMs('Jane', 'Jones'); //=> 'Hello, Ms. Jane Jones!'

partialRight:与partial类似,但数组指定的参数为最右边的参数。


var greet = (salutation, title, firstName, lastName) =>
salutation + ', ' + title + ' ' + firstName + ' ' + lastName + '!'; var greetMsJaneJones = R.partialRight(greet, ['Ms.', 'Jane', 'Jones']);
greetMsJaneJones('Hello') // 'Hello, Ms. Jane Jones!'

useWith:接受一个函数fn和一个函数数组fnList作为参数,返回fn的柯里化版本。该新函数的参数,先分别经过对应的fnList成员处理,再传入fn执行。


var decreaseOne = x => x - 1;
var increaseOne = x => x + 1; R.useWith(Math.pow, [decreaseOne, increaseOne])(3, 4) // 32
R.useWith(Math.pow, [decreaseOne, increaseOne])(3)(4) // 32

memoize:返回一个函数,会缓存每一次的运行结果。


var productOfArr = arr => {
var product = 1;
arr.forEach(i => product *= i);
return product;
};
var count = 0;
var factorial = R.memoize(n => {
count += 1;
return productOfArr(R.range(1, n + 1));
});
factorial(5) // 120
factorial(5) // 120
factorial(5) // 120
count // 1

complement:返回一个新函数,如果原函数返回true,该函数返回false;如果原函数返回false,该函数返回true


var gt10 = x => x > 10;
var lte10 = R.complement(gt10);
gt10(7) // false
lte10(7) // true

5.3 函数的执行

binary:参数函数执行时,只传入最前面两个参数。


var takesThreeArgs = function(a, b, c) {
return [a, b, c];
}; var takesTwoArgs = R.binary(takesThreeArgs);
takesTwoArgs(1, 2, 3) // [1, 2, undefined]

tap:将一个值传入指定函数,并返回该值。


var sayX = x => console.log('x is ' + x);
R.tap(sayX)(100) // 100 R.pipe(
R.assoc('a', 2),
R.tap(console.log),
R.assoc('a', 3)
)({a: 1})
// {a: 3}

zipWith:将两个数组对应位置的值,一起作为参数传入某个函数。


var f = (x, y) => {
// ...
};
R.zipWith(f, [1, 2, 3])(['a', 'b', 'c'])
// [f(1, 'a'), f(2, 'b'), f(3, 'c')]

apply:将数组转成参数序列,传入指定函数。


var nums = [1, 2, 3, -99, 42, 6, 7];
R.apply(Math.max)(nums) // 42

applySpec:返回一个模板函数,该函数会将参数传入模板内的函数执行,然后将执行结果填充到模板。


var getMetrics = R.applySpec({
sum: R.add,
nested: { mul: R.multiply }
}); getMetrics(2, 4) // { sum: 6, nested: { mul: 8 } }

ascend:返回一个升序排列的比较函数,主要用于排序。


var byAge = R.ascend(R.prop('age'));
var people = [
// ...
];
var peopleByYoungestFirst = R.sort(byAge)(people);

descend:返回一个降序排列的比较函数,主要用于排序。


var byAge = R.descend(R.prop('age'));
var people = [
// ...
];
var peopleByOldestFirst = R.sort(byAge)(people);

六、数组

6.1 数组的特征判断

contains:如果包含某个成员,返回true


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

all:所有成员都满足指定函数时,返回true,否则返回false


var equals3 = R.equals(3);
R.all(equals3)([3, 3, 3, 3]) // true
R.all(equals3)([3, 3, 1, 3]) // false

any:只要有一个成员满足条件,就返回true


var lessThan0 = R.flip(R.lt)(0);
var lessThan2 = R.flip(R.lt)(2);
R.any(lessThan0)([1, 2]) // false
R.any(lessThan2)([1, 2]) // true

none:没有成员满足条件时,返回true


var isEven = n => n % 2 === 0; R.none(isEven)([1, 3, 5, 7, 9, 11]) // true
R.none(isEven)([1, 3, 5, 7, 8, 11]) // false

6.2 数组的截取和添加

head:返回数组的第一个成员。


R.head(['fi', 'fo', 'fum']) // 'fi'
R.head([]) // undefined
R.head('abc') // 'a'
R.head('') // ''

last:返回数组的最后一个成员。


R.last(['fi', 'fo', 'fum']) // 'fum'
R.last([]) // undefined
R.last('abc') // 'c'
R.last('') // ''

tail:返回第一个成员以外的所有成员组成的新数组。


R.tail([1, 2, 3]) // [2, 3]
R.tail([1, 2]) // [2]
R.tail([1]) // []
R.tail([]) // [] R.tail('abc') // 'bc'
R.tail('ab') // 'b'
R.tail('a') // ''
R.tail('') // ''

init:返回最后一个成员以外的所有成员组成的新数组。


R.init([1, 2, 3]) // [1, 2]
R.init([1, 2]) // [1]
R.init([1]) // []
R.init([]) // [] R.init('abc') // 'ab'
R.init('ab') // 'a'
R.init('a') // ''
R.init('') // ''

nth:取出指定位置的成员。


var list = ['foo', 'bar', 'baz', 'quux'];
R.nth(1)(list) // 'bar'
R.nth(-1)(list) // 'quux'
R.nth(-99)(list) // undefined R.nth(2)('abc') // 'c'
R.nth(3)('abc') // ''

take:取出前 n 个成员。


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(4)(['foo', 'bar', 'baz']) // ['foo', 'bar', 'baz']
R.take(3)('ramda') // 'ram'

takeLast:取出后 n 个成员。


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(4)(['foo', 'bar', 'baz']) // ['foo', 'bar', 'baz']
R.takeLast(3)('ramda') // 'mda'

slice:从起始位置(包括)开始,到结束位置(不包括)为止,从原数组截取出一个新数组。


R.slice(1, 3)(['a', 'b', 'c', 'd']) // ['b', 'c']
R.slice(1, Infinity)(['a', 'b', 'c', 'd']) // ['b', 'c', 'd']
R.slice(0, -1)(['a', 'b', 'c', 'd']) // ['a', 'b', 'c']
R.slice(-3, -1)(['a', 'b', 'c', 'd']) // ['b', 'c']
R.slice(0, 3)('ramda') // 'ram'

remove:移除开始位置后的n个成员。


R.remove(2, 3)([1,2,3,4,5,6,7,8]) // [1,2,6,7,8]

insert:在指定位置插入给定值。


R.insert(2, 'x')([1,2,3,4]) // [1,2,'x',3,4]

insertAll:在指定位置,插入另一个数组的所有成员。


R.insertAll(2,['x','y','z'])([1,2,3,4]) // [1,2,'x','y','z',3,4]

prepend:在数组头部插入一个成员


R.prepend('fee')(['fi', 'fo', 'fum'])
// ['fee', 'fi', 'fo', 'fum']

append:在数组尾部追加新的成员。


R.append('tests')(['write', 'more']) // ['write', 'more', 'tests']
R.append('tests')([]) // ['tests']
R.append(['tests'])(['write', 'more']) // ['write', 'more', ['tests']]

intersperse:在数组成员之间插入表示分隔的成员。


R.intersperse('n')(['ba', 'a', 'a'])
// ['ba', 'n', 'a', 'n', 'a']

join:将数组合并成一个字符串,并在成员之间插入分隔符。


R.join('|')([1, 2, 3]) // '1|2|3'

6.3 数组的过滤

filter:过滤出符合条件的成员。


var isEven = n => n % 2 === 0;
R.filter(isEven)([1, 2, 3, 4]) // [2, 4]

reject:过滤出所有不满足条件的成员。


var isOdd = (n) => n % 2 === 1;
R.reject(isOdd)([1, 2, 3, 4]) // [2, 4]

takeWhile: 一旦满足条件,后面的成员都会被过滤。


var isNotFour = x => x !== 4;
R.takeWhile(isNotFour)([1, 2, 3, 4, 3, 2, 1]) // [1, 2, 3]

dropWhile:一旦不满足条件,取出剩余的所有成员。


var lteTwo = x => x <= 2;
R.dropWhile(lteTwo)([1, 2, 3, 4, 3, 2, 1])
// [3, 4, 3, 2, 1]

without:返回指定值以外的成员。


R.without([1, 2])([1, 2, 1, 3, 4])
// [3, 4]

6.4 单数组运算

countBy:对每个成员执行指定函数以后,返回一个对象,表示各种执行结果分别包含多少成员。


var numbers = [1.0, 1.1, 1.2, 2.0, 3.0, 2.2];
R.countBy(Math.floor)(numbers) // {'1': 3, '2': 2, '3': 1} var letters = ['a', 'b', 'A', 'a', 'B', 'c'];
R.countBy(R.toLower)(letters) // {'a': 3, 'b': 2, 'c': 1}

splitAt:在给定位置,将原数组分成两个部分。


R.splitAt(1)([1, 2, 3]) // [[1], [2, 3]]
R.splitAt(5)('hello world') // ['hello', ' world']
R.splitAt(-1)('foobar') // ['fooba', 'r']

splitEvery:按照指定的个数,将原数组分成多个部分。


R.splitEvery(3)([1, 2, 3, 4, 5, 6, 7])
// [[1, 2, 3], [4, 5, 6], [7]] R.splitEvery(3)('foobarbaz')
// ['foo', 'bar', 'baz']

splitWhen:以第一个满足指定函数的成员为界,将数组分成两个部分。


R.splitWhen(R.equals(2))([1, 2, 3, 1, 2, 3])
// [[1], [2, 3, 1, 2, 3]]

aperture:每个成员与其后给定数量的成员分成一组,这些组构成一个新的数组。


R.aperture(3)([1, 2, 3, 4, 5, 6, 7])
// [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]]

partition:根据是否满足指定函数,将成员分区。


R.partition(R.contains('s'))(['sss', 'ttt', 'foo', 'bars'])
// => [ [ 'sss', 'bars' ], [ 'ttt', 'foo' ] ]

indexOf:某个值在数组中第一次出现的位置。


R.indexOf(3)([1,2,3,4]) // 2
R.indexOf(10)([1,2,3,4]) // -1

lastIndexOf:某个值在数组中最后一次出现的位置。


R.lastIndexOf(3)([-1,3,3,0,1,2,3,4]) // 6
R.lastIndexOf(10)([1,2,3,4]) // -1

map:数组的每个成员依次执行某个函数。


var double = x => x * 2;
R.map(double)([1, 2, 3]) // [2, 4, 6]

mapIndexed:与map类似,区别是遍历函数可以额外获得两个参数:索引位置和原数组。


var 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:数组的每个成员依次执行某个函数,总是返回原数组。


var printXPlusFive = x => console.log(x + 5);
R.forEach(printXPlusFive, [1, 2, 3]) // [1, 2, 3]
// logs 6
// logs 7
// logs 8

reduce:数组成员依次执行指定函数,每一次的运算结果都会进入一个累积变量。


var mySubtract = function (a, b) {
return a - b;
};
R.reduce(mySubtract, 0)([1, 2, 3, 4]) // -10

reduceRight:与reduce类似,区别是数组成员从左到右执行。


R.reduceRight(R.subtract, 0)([1, 2, 3, 4]) // -2

reduceWhile:与reduce类似,区别是有一个判断函数,一旦数组成员不符合条件,就停止累积。


var isOdd = (acc, x) => x % 2 === 1;
var xs = [1, 3, 5, 60, 777, 800];
R.reduceWhile(isOdd, R.add, 0)(xs) // 9 var ys = [2, 4, 6];
R.reduceWhile(isOdd, R.add, 111)(ys) // 111

sort:按照给定函数,对数组进行排序。


var diff = function(a, b) { return a - b; };
R.sort(diff)([4,2,7,5])
// [2, 4, 5, 7]

sortWith:按照给定的一组函数,进行多重排序。


var alice = {
name: 'alice',
age: 40
};
var bob = {
name: 'bob',
age: 30
};
var clara = {
name: 'clara',
age: 40
};
var people = [clara, bob, alice];
var ageNameSort = R.sortWith([
R.descend(R.prop('age')),
R.ascend(R.prop('name'))
]);
ageNameSort(people); //=> [alice, clara, bob]

adjust:对指定位置的成员执行给定的函数。


R.adjust(R.add(10), 1)([1, 2, 3]) // [1, 12, 3]
R.adjust(R.add(10),1)([1, 2, 3]) // [1, 12, 3]

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"]

flatten:将嵌套数组铺平。


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]

groupBy:将数组成员依次按照指定条件两两比较,并按照结果将所有成员放入子数组。


R.groupWith(R.equals)([0, 1, 1, 2, 3, 5, 8, 13, 21])
// [[0], [1, 1], [2], [3], [5], [8], [13], [21]] 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]] R.groupWith(R.eqBy(isVowel), 'aestiou')
//=> ['ae', 'st', 'iou']

6.5 双数组运算

concat:将两个数组合并成一个数组。


R.concat('ABC')('DEF') // 'ABCDEF'
R.concat([4, 5, 6])([1, 2, 3]) // [4, 5, 6, 1, 2, 3]
R.concat([])([]) // []

zip:将两个数组指定位置的成员放在一起,生成一个新数组。


R.zip([1, 2, 3])(['a', 'b', 'c'])
// [[1, 'a'], [2, 'b'], [3, 'c']]

zipObj:将两个数组指定位置的成员分别作为键名和键值,生成一个新对象。


R.zipObj(['a', 'b', 'c'])([1, 2, 3])
// {a: 1, b: 2, c: 3}

xprod:将两个数组的成员两两混合,生成一个新数组。


R.xprod([1, 2])(['a', 'b'])
// [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]

intersection:返回两个数组相同的成员组成的新数组。


R.intersection([1,2,3,4], [7,6,5,4,3]) // [4, 3]

intersectionWith:返回经过某种运算,有相同结果的两个成员。


var buffaloSpringfield = [
{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'}
];
var csny = [
{id: 204, name: 'David Crosby'},
{id: 456, name: 'Stephen Stills'},
{id: 539, name: 'Graham Nash'},
{id: 177, name: 'Neil Young'}
]; R.intersectionWith(R.eqBy(R.prop('id')),buffaloSpringfield)(csny)
// [{id: 456, name: 'Stephen Stills'}, {id: 177, name: 'Neil Young'}]

difference:返回第一个数组不包含在第二个数组里面的成员。


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}]

differenceWith:返回执行指定函数后,第一个数组里面不符合条件的所有成员。


var cmp = (x, y) => x.a === y.a;
var l1 = [{a: 1}, {a: 2}, {a: 3}];
var l2 = [{a: 3}, {a: 4}];
R.differenceWith(cmp, l1)(l2) // [{a: 1}, {a: 2}]

symmetricDifference:返回两个数组的非共有成员所组成的一个新数组。


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]

symmetricDifferenceWith:根据指定条件,返回两个数组所有运算结果不相等的成员所组成的新数组。


var eqA = R.eqBy(R.prop('a'));
var l1 = [{a: 1}, {a: 2}, {a: 3}, {a: 4}];
var l2 = [{a: 3}, {a: 4}, {a: 5}, {a: 6}];
R.symmetricDifferenceWith(eqA, l1, l2) // [{a: 1}, {a: 2}, {a: 5}, {a: 6}]

6.6 复合数组

find:返回符合指定条件的成员。


var xs = [{a: 1}, {a: 2}, {a: 3}];
R.find(R.propEq('a', 2))(xs) // {a: 2}
R.find(R.propEq('a', 4))(xs) // undefined

findIndex:返回符合指定条件的成员的位置。


var xs = [{a: 1}, {a: 2}, {a: 3}];
R.findIndex(R.propEq('a', 2))(xs) // 1
R.findIndex(R.propEq('a', 4))(xs) // -1

findLast:返回最后一个符合指定条件的成员。


var 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

findLastIndex:返回最后一个符合指定条件的成员的位置。


var 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

pluck:取出数组成员的某个属性,组成一个新数组。


R.pluck('a')([{a: 1}, {a: 2}]) // [1, 2]
R.pluck(0)([[1, 2], [3, 4]]) // [1, 3]

project:取出数组成员的多个属性,组成一个新数组。


var abby = {name: 'Abby', age: 7, hair: 'blond', grade: 2};
var fred = {name: 'Fred', age: 12, hair: 'brown', grade: 7};
var kids = [abby, fred];
R.project(['name', 'grade'])(kids)
// [{name: 'Abby', grade: 2}, {name: 'Fred', grade: 7}]

transpose:将每个成员相同位置的值,组成一个新数组。


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']] R.transpose([[10, 11], [20], [], [30, 31, 32]])
// [[10, 20, 30], [11, 31], [32]]

mergeAll:将数组的成员合并成一个对象。


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}

fromPairs:将嵌套数组转为一个对象。


R.fromPairs([['a', 1], ['b', 2], ['c', 3]])
// {a: 1, b: 2, c: 3}

groupBy:将数组成员按照指定条件分组。


var byGrade = R.groupBy(function(student) {
var score = student.score;
return score < 65 ? 'F' :
score < 70 ? 'D' :
score < 80 ? 'C' :
score < 90 ? 'B' : 'A';
});
var 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}]
// }

sortBy:根据成员的某个属性排序。


var sortByFirstItem = R.sortBy(R.prop(0));
sortByFirstItem([[-1, 1], [-2, 2], [-3, 3]])
// [[-3, 3], [-2, 2], [-1, 1]] var sortByNameCaseInsensitive = R.sortBy(
R.compose(R.toLower, R.prop('name'))
);
var alice = {name: 'ALICE', age: 101};
var bob = {name: 'Bob', age: -10};
var clara = {name: 'clara', age: 314.159};
var people = [clara, bob, alice];
sortByNameCaseInsensitive(people)
// [alice, bob, clara]

七、对象

7.1 对象的特征判断

has: 返回一个布尔值,表示对象自身是否具有该属性。


var hasName = R.has('name')
hasName({name: 'alice'}) //=> true
hasName({name: 'bob'}) //=> true
hasName({}) //=> false var point = {x: 0, y: 0};
var pointHas = R.has(R.__, point);
pointHas('x') // true
pointHas('y') // true
pointHas('z') // false

hasIn:返回一个布尔值,表示对象自身或原型链上是否具有某个属性。


function Rectangle(width, height) {
this.width = width;
this.height = height;
}
Rectangle.prototype.area = function() {
return this.width * this.height;
}; var square = new Rectangle(2, 2);
R.hasIn('width')(square) // true
R.hasIn('area')(square) // true

propEq:如果属性等于给定值,返回true


var abby = {name: 'Abby', age: 7, hair: 'blond'};
var fred = {name: 'Fred', age: 12, hair: 'brown'};
var rusty = {name: 'Rusty', age: 10, hair: 'brown'};
var alois = {name: 'Alois', age: 15, disposition: 'surly'};
var kids = [abby, fred, rusty, alois];
var hasBrownHair = R.propEq('hair', 'brown');
R.filter(hasBrownHair)(kids) // [fred, rusty]

whereEq:如果属性等于给定值,返回true


var 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

where:如果各个属性都符合指定条件,返回true


var pred = R.where({
a: R.equals('foo'),
b: R.complement(R.equals('bar')),
x: R.gt(__, 10),
y: R.lt(__, 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

7.2 对象的过滤

omit:过滤指定属性。


R.omit(['a', 'd'])({a: 1, b: 2, c: 3, d: 4})
// {b: 2, c: 3}

filter:返回所有满足条件的属性


var isEven = n => n % 2 === 0;
R.filter(isEven)({a: 1, b: 2, c: 3, d: 4}) // {b: 2, d: 4}

reject:返回所有不满足条件的属性


var isOdd = (n) => n % 2 === 1;
R.reject(isOdd)({a: 1, b: 2, c: 3, d: 4})
// {b: 2, d: 4}

7.3 对象的截取

dissoc:过滤指定属性。


R.dissoc('b')({a: 1, b: 2, c: 3})
// {a: 1, c: 3}

assoc:添加或改写某个属性。


R.assoc('c', 3)({a: 1, b: 2})
// {a: 1, b: 2, c: 3}

partition:根据属性值是否满足给定条件,将属性分区。


R.partition(R.contains('s'))({ a: 'sss', b: 'ttt', foo: 'bars' })
// [ { a: 'sss', foo: 'bars' }, { b: 'ttt' } ]

pick:返回指定属性组成的新对象


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}

pickAll:与pick类似,但会包括不存在的属性。


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}

pickBy:返回符合条件的属性


var isUpperCase = (val, key) => key.toUpperCase() === key;
R.pickBy(isUpperCase)({a: 1, b: 2, A: 3, B: 4})
// {A: 3, B: 4}

keys:返回对象自身属性的属性名组成的新数组。


R.keys({a: 1, b: 2, c: 3}) // ['a', 'b', 'c']

keysIn:返回对象自身的和继承的属性的属性名组成的新数组。


var F = function() { this.x = 'X'; };
F.prototype.y = 'Y';
var f = new F();
R.keysIn(f) // ['x', 'y']

values:返回对象自身的属性的属性值组成的数组。


R.values({a: 1, b: 2, c: 3}); //=> [1, 2, 3]

valuesIn:返回对象自身的和继承的属性的属性值组成的数组。


var F = function() { this.x = 'X'; };
F.prototype.y = 'Y';
var f = new F();
R.valuesIn(f) // ['X', 'Y']

invertObj:将属性值和属性名互换。如果多个属性的属性值相同,只返回最后一个属性。


var raceResultsByFirstName = {
first: 'alice',
second: 'jake',
third: 'alice',
};
R.invertObj(raceResultsByFirstName)
// {"alice": "third", "jake": "second"}

invert:将属性值和属性名互换,每个属性值对应一个数组。


var raceResultsByFirstName = {
first: 'alice',
second: 'jake',
third: 'alice',
};
R.invert(raceResultsByFirstName)
// { 'alice': ['first', 'third'], 'jake':['second'] }

7.4 对象的运算

prop:返回对象的指定属性


R.prop('x')({x: 100})
// 100 R.prop('x')({})
// undefined

map:对象的所有属性依次执行某个函数。


var double = x => x * 2;
R.map(double)({x: 1, y: 2, z: 3})
// {x: 2, y: 4, z: 6}

mapObjIndexed:与map类似,但是会额外传入属性名和整个对象。


var values = { x: 1, y: 2, z: 3 };
var prependKeyAndDouble = (num, key, obj) => key + (num * 2); R.mapObjIndexed(prependKeyAndDouble)(values)
// { x: 'x2', y: 'y4', z: 'z6' }

forEachObjIndexed:每个属性依次执行给定函数,给定函数的参数分别是属性值和属性名,返回原对象。


var printKeyConcatValue = (value, key) => console.log(key + ':' + value);
R.forEachObjIndexed(printKeyConcatValue)({x: 1, y: 2}) // {x: 1, y: 2}
// logs x:1
// logs y:2

merge:合并两个对象,如果有同名属性,后面的值会覆盖掉前面的值。


R.merge({ 'name': 'fred', 'age': 10 })({ 'age': 40 })
// { 'name': 'fred', 'age': 40 } var resetToDefault = R.merge(R.__, {x: 0});
resetToDefault({x: 5, y: 2}) // {x: 0, y: 2}

mergeWith:合并两个对象,如果有同名属性,会使用指定的函数处理。


R.mergeWith(
R.concat,
{ a: true, values: [10, 20] },
{ b: true, values: [15, 35] }
);
// { a: true, b: true, values: [10, 20, 15, 35] }

eqProps:比较两个对象的指定属性是否相等。


var o1 = { a: 1, b: 2, c: 3, d: 4 };
var o2 = { a: 10, b: 20, c: 3, d: 40 };
R.eqProps('a', o1)(o2) // false
R.eqProps('c', o1)(o2) // true

R.evolve:对象的属性分别经过一组函数的处理,返回一个新对象。


var tomato = {
firstName: ' Tomato ',
data: {elapsed: 100, remaining: 1400},
id: 123
};
var transformations = {
firstName: R.trim,
lastName: R.trim, // 不会被调用
data: {elapsed: R.add(1), remaining: R.add(-1)}
};
R.evolve(transformations)(tomato)
// {
// firstName: 'Tomato',
// data: {elapsed: 101, remaining: 1399},
// id: 123
// }

7.5 复合对象

path:取出数组中指定路径的值。


R.path(['a', 'b'], {a: {b: 2}}) // 2
R.path(['a', 'b'], {c: {b: 2}}) // undefined

pathEq:返回指定路径的值符合条件的成员


var user1 = { address: { zipCode: 90210 } };
var user2 = { address: { zipCode: 55555 } };
var user3 = { name: 'Bob' };
var users = [ user1, user2, user3 ];
var isFamous = R.pathEq(['address', 'zipCode'], 90210);
R.filter(isFamous)(users) // [ user1 ]

assocPath:添加或改写指定路径的属性的值。


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}}}

(完)

[转] Ramda 函数库参考教程的更多相关文章

  1. 从函数式编程到Ramda函数库(二)

    Ramda 基本的数据结构都是原生 JavaScript 对象,我们常用的集合是 JavaScript 的数组.Ramda 还保留了许多其他原生 JavaScript 特性,例如,函数是具有属性的对象 ...

  2. 从函数式编程到Ramda函数库(一)

    函数式编程是种编程方式,它将电脑运算视为函数的计算.函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值).和指令式编程相比, ...

  3. Linux C函数库参考手册

    目录 第1章 字符测试函数 isalnum(测试字符是否为英文字母或数字) isalpha(测试字符是否为英文字母) isascii(测试字符是否为ascii码字符) isblank(测试字符是否为空 ...

  4. 【转】Linux C函数库参考

    asctime(将时间和日期以字符串格式表示)clock(取得进程占用CPU的大约时间)ctime(将时间和日期以字符串格式表示)difftime(计算时间差距)ftime(取得目前的时间和日期)ge ...

  5. Ruby 参考教程

    Ruby 参考教程 https://www.ruby-lang.org/zh_cn/documentation/ http://ruby-doc.org/docs/ https://ruby-chin ...

  6. 【C语言入门教程】1.2 函数库 和 链接

    程序员可以不需要从头开始设计每一个函数,完成用C语言命令所实现的函数非常罕见.因为所有的C语言编辑器都提供能完成各种常见任务函数,如printf()函数等.C语言编译器的实现者已经编写了大部分常见的通 ...

  7. 【C语言入门教程】5.6 函数库和文件

    函数库是为代码复用建立的,将同一类型,需要在不同的程序里使用的函数放置在一起,就组成了一个函数库.如 C 语言的标准库,它集合了开发者常用的函数.开发者自行编写的函数也可以组成函数库,通常称之为自定义 ...

  8. cgic: CGI的C函数库

    下载回源码包以后,就3个文件:cgic.c      函数库capture.c   一个很简单的CGI例子,仅仅输出两行提示文字cgictest.c  一个演示读取form表单数据的CGI例子 首先在 ...

  9. 翻译连载 | 附录 C:函数式编程函数库-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

随机推荐

  1. VUE项目中使用mint-ui框架总结

    针对PC端,element-ui可谓是首选了,UI体验效果很好. element-ui 框架官网:http://element.eleme.io/#/zh-CN/component/installat ...

  2. Nginx-http_proxy_module模块

    Nginx 反向代理之 http_proxy_module 模块 proxy_pass指定属于 ngx_http_proxy_module 模块,此模块可以将请求转发到另一台服务器,在实际的反向代理工 ...

  3. vim命令替换操作

    替换当前行第一个 vivian为sky :s/vivian/sky/ 替换当前行所有 vivian为sky :s/vivian/sky/g 替换第 n 行开始到最后一行中,每一行的第一个vivian为 ...

  4. CF235B Let's Play Osu! 期望DP

    貌似是一道很裸的期望\(DP\).直接说思路: 设\(f[i]\)表示到\(i\)位置时的期望分数,但是只有\(f[i]\)的话我们发现是无法转移的,我们还需要知道到\(i\)位置时的期望连续长度,于 ...

  5. passat product list

    s end

  6. 【游戏开发】基于VS2017的OpenGL开发环境搭建

    一.简介 最近,马三买了两本有关于“计算机图形学”的书籍,准备在工作之余鼓捣鼓捣图形学和OpenGL编程,提升自己的价值(奔着学完能涨一波工资去的).俗话说得好,“工欲善其事,必先利其器”.想学习图形 ...

  7. Window7 定制 Explore中的右键菜单

    win+R 命令  运行 regedit ,打开注册表 在  HKEY_CLASSES_ROOT\*\shell\VisualCode下创建针对文件的新增命令 command 在HKEY_CLASSE ...

  8. unet 网络接受任意大小的输入

    将网络的输入定义的placeholder 大小设为:[None,None,c], 不设置固定的大小. 但是出现问题: 前层特征图与后层特征图进行组合时,尺寸大小不一致: [32, 60, 256] 和 ...

  9. 深入理解line-height与vertical-align——前端布局常用属性

    line-height.font-size.vertical-align是设置行内元素布局的关键属性.这三个属性是相互依赖的关系,改变行间距离.设置垂直对齐等都需要它们的通力合作.下面将主要介绍lin ...

  10. django中的分页标签

    class Pagination: def __init__(self, page, page_total, per_one_page=15, max_page=11): ''' :param pag ...