Ramda 函数库参考教程

sun_hj189 7年前
   <p>学习函数式编程的过程中,我接触到了 <a href="/misc/goto?guid=4959741109309702161" rel="nofollow,noindex">Ramda.js</a> 。</p>    <p>我发现,这是一个很重要的库,提供了许多有用的方法,每个 JavaScript 程序员都应该掌握这个工具。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/be22674cab9ffcb327ee3f215e28f6c7.jpg"></p>    <p>你可能会问, Underscore 和 Lodash 已经这么流行了,为什么还要学习好像雷同的 Ramda 呢?</p>    <p>回答是,前两者的参数位置不对,把处理的数据放到了第一个参数。</p>    <pre>  <code class="language-javascript">var square = n => n * n;  _.map([4, 8], square) // [16, 64]</code></pre>    <p>上面代码中, _.map 的第一个参数 [4, 8] 是要处理的数据,第二个参数 square 是数据要执行的运算。</p>    <p>Ramda 的数据一律放在最后一个参数,理念是" <strong>function first,data last</strong> "。</p>    <pre>  <code class="language-javascript">var R = require('ramda');  R.map(square, [4, 8]) // [16, 64]</code></pre>    <p>为什么 Underscore 和 Lodash 是错的,而 Ramda 是对的?这放在下一篇文章详细解释,今天我主要介绍 Ramda 提供的几十个方法。这是理解以后的内容所必须的。</p>    <p>除了数据放在最后一个参数,Ramda 还有一个特点: <strong>所有方法都支持柯里化</strong> 。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c1c28111a22e812546b7e77559381245.jpg"></p>    <p>也就是说,所有多参数的函数,默认都可以单参数使用。</p>    <pre>  <code class="language-javascript">// 写法一  R.map(square, [4, 8])    // 写法二  R.map(square)([4, 8])  // 或者  var mapSquare = R.map(square);  mapSquare([4, 8]);</code></pre>    <p>上面代码中,写法一是多参数版本,写法二是柯里化以后的单参数版本。Ramda 都支持,并且推荐使用第二种写法。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/79ea467aa6d7bc44a5573c0729a34a20.jpg"></p>    <p>由于这两个特点,使得 Ramda 成为 JavaScript 函数式编程最理想的工具库。今天,我先介绍它的 API,下一次再介绍这些方法如何用于实战。我保证,一旦你理解了它的运算模型,就一定会认同这才是正确的计算方法。</p>    <h2>Ramda API 介绍</h2>    <h2>一、比较运算</h2>    <p>gt :判断第一个参数是否大于第二个参数。</p>    <pre>  <code class="language-javascript">R.gt(2)(1) // true  R.gt('a')('z') // false</code></pre>    <p>gte :判断第一个参数是否大于等于第二个参数。</p>    <pre>  <code class="language-javascript">R.gte(2)(2) // true  R.gte('a')('z') // false</code></pre>    <p>lt :判断第一个参数是否小于第二个参数。</p>    <pre>  <code class="language-javascript">R.lt(2)(1) // false  R.lt('a')('z') // true</code></pre>    <p>lte :判断第一个参数是否小于等于第二个参数。</p>    <pre>  <code class="language-javascript">R.lte(2)(2) // true  R.lte('a')('z') // true</code></pre>    <p>equals :比较两个值是否相等(支持对象的比较)。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>eqBy :比较两个值传入指定函数的运算结果是否相等。</p>    <pre>  <code class="language-javascript">R.eqBy(Math.abs, 5)(-5)  // true</code></pre>    <h2>二、数学运算</h2>    <p>add :返回两个值的和。</p>    <pre>  <code class="language-javascript">R.add(7)(10) // 17</code></pre>    <p>subtract :返回第一个参数减第二个参数的差。</p>    <pre>  <code class="language-javascript">R.subtract(10)(8) // 2</code></pre>    <p>multiply :返回两个值的差。</p>    <pre>  <code class="language-javascript">R.multiply(2)(5)  // 10</code></pre>    <p>divide :返回第一个参数除以第二个参数的商。</p>    <pre>  <code class="language-javascript">R.divide(71)(100) // 0.71</code></pre>    <h2>三、逻辑运算</h2>    <p>either :接受两个函数作为参数,只要有一个返回 true ,就返回 true ,否则返回 false 。相当于 || 运算。</p>    <pre>  <code class="language-javascript">var gt10 = x => x > 10;  var even = x => x % 2 === 0;    var f = R.either(gt10, even);  f(101) // true  f(8) // true</code></pre>    <p>both :接受两个函数作为参数,只有它们都返回 true ,才返回 true ,否则返回 false ,相当于 && 运算。</p>    <pre>  <code class="language-javascript">var gt10 = x => x > 10;  var even = x => x % 2 === 0;    var f = R.both(gt10, even);  f(15) // false  f(30) // true</code></pre>    <p>allPass :接受一个函数数组作为参数,只有它们都返回 true ,才返回 true ,否则返回 false 。</p>    <pre>  <code class="language-javascript">var gt10 = x => x > 10;  var even = x => x % 2 === 0;    var isEvenAndGt10 = R.allPass([gt10, even]);  isEvenAndGt10(15) // false  isEvenAndGt10(30) // true</code></pre>    <h2>四、字符串</h2>    <p>split :按照指定分隔符将字符串拆成一个数组。</p>    <pre>  <code class="language-javascript">R.split('.')('a.b.c.xyz.d')  // ['a', 'b', 'c', 'xyz', 'd']</code></pre>    <p>test :判断一个字符串是否匹配给定的正则表达式。</p>    <pre>  <code class="language-javascript">R.test(/^x/)('xyz')  // true    R.test(/^y/)('xyz')  // false</code></pre>    <p>match :返回一个字符串的匹配结果。</p>    <pre>  <code class="language-javascript">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"</code></pre>    <h2>五、函数</h2>    <h3>5.1 函数的合成</h3>    <p>compose :将多个函数合并成一个函数,从右到左执行。</p>    <pre>  <code class="language-javascript">R.compose(Math.abs, R.add(1), R.multiply(2))(-4) // 7</code></pre>    <p>pipe :将多个函数合并成一个函数,从左到右执行。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>converge :接受两个参数,第一个参数是函数,第二个参数是函数数组。传入的值先使用第二个参数包含的函数分别处理以后,再用第一个参数处理前一步生成的结果。</p>    <pre>  <code class="language-javascript">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')</code></pre>    <h3>5.2 柯里化</h3>    <p>curry :将多参数的函数,转换成单参数的形式。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>partial :允许多参数的函数接受一个数组,指定最左边的部分参数。</p>    <pre>  <code class="language-javascript">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!'</code></pre>    <p>partialRight :与 partial 类似,但数组指定的参数为最右边的参数。</p>    <pre>  <code class="language-javascript">var greet = (salutation, title, firstName, lastName) =>    salutation + ', ' + title + ' ' + firstName + ' ' + lastName + '!';    var greetMsJaneJones = R.partialRight(greet, ['Ms.', 'Jane', 'Jones']);  greetMsJaneJones('Hello') // 'Hello, Ms. Jane Jones!'</code></pre>    <p>useWith :接受一个函数 fn 和一个函数数组 fnList 作为参数,返回 fn 的柯里化版本。该新函数的参数,先分别经过对应的 fnList 成员处理,再传入 fn 执行。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>memoize :返回一个函数,会缓存每一次的运行结果。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>complement :返回一个新函数,如果原函数返回 true ,该函数返回 false ;如果原函数返回 false ,该函数返回 true 。</p>    <pre>  <code class="language-javascript">var gt10 = x => x > 10;  var lte10 = R.complement(gt10);  gt10(7) // false  lte10(7) // true</code></pre>    <h3>5.3 函数的执行</h3>    <p>binary :参数函数执行时,只传入最前面两个参数。</p>    <pre>  <code class="language-javascript">var takesThreeArgs = function(a, b, c) {    return [a, b, c];  };    var takesTwoArgs = R.binary(takesThreeArgs);  takesTwoArgs(1, 2, 3) // [1, 2, undefined]</code></pre>    <p>tap :将一个值传入指定函数,并返回该值。</p>    <pre>  <code class="language-javascript">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}</code></pre>    <p>zipWith :将两个数组对应位置的值,一起作为参数传入某个函数。</p>    <pre>  <code class="language-javascript">var f = (x, y) => {    // ...  };  R.zipWith(f, [1, 2, 3])(['a', 'b', 'c'])  // [f(1, 'a'), f(2, 'b'), f(3, 'c')]</code></pre>    <p>apply :将数组转成参数序列,传入指定函数。</p>    <pre>  <code class="language-javascript">var nums = [1, 2, 3, -99, 42, 6, 7];  R.apply(Math.max)(nums) // 42</code></pre>    <p>applySpec :返回一个模板函数,该函数会将参数传入模板内的函数执行,然后将执行结果填充到模板。</p>    <pre>  <code class="language-javascript">var getMetrics = R.applySpec({    sum: R.add,    nested: { mul: R.multiply }  });    getMetrics(2, 4) // { sum: 6, nested: { mul: 8 } }</code></pre>    <p>ascend :返回一个升序排列的比较函数,主要用于排序。</p>    <pre>  <code class="language-javascript">var byAge = R.ascend(R.prop('age'));  var people = [    // ...  ];  var peopleByYoungestFirst = R.sort(byAge)(people);</code></pre>    <p>descend :返回一个降序排列的比较函数,主要用于排序。</p>    <pre>  <code class="language-javascript">var byAge = R.descend(R.prop('age'));  var people = [    // ...  ];  var peopleByOldestFirst = R.sort(byAge)(people);</code></pre>    <h2>六、数组</h2>    <h3>6.1 数组的特征判断</h3>    <p>contains :如果包含某个成员,返回 true 。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>all :所有成员都满足指定函数时,返回 true ,否则返回 false</p>    <pre>  <code class="language-javascript">var equals3 = R.equals(3);  R.all(equals3)([3, 3, 3, 3]) // true  R.all(equals3)([3, 3, 1, 3]) // false</code></pre>    <p>any :只要有一个成员满足条件,就返回 true 。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>none :没有成员满足条件时,返回 true 。</p>    <pre>  <code class="language-javascript">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</code></pre>    <h3>6.2 数组的截取和添加</h3>    <p>head :返回数组的第一个成员。</p>    <pre>  <code class="language-javascript">R.head(['fi', 'fo', 'fum']) // 'fi'  R.head([])  // undefined  R.head('abc') // 'a'  R.head('') // ''</code></pre>    <p>last :返回数组的最后一个成员。</p>    <pre>  <code class="language-javascript">R.last(['fi', 'fo', 'fum']) // 'fum'  R.last([]) // undefined  R.last('abc') // 'c'  R.last('') // ''</code></pre>    <p>tail :返回第一个成员以外的所有成员组成的新数组。</p>    <pre>  <code class="language-javascript">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('')     // ''</code></pre>    <p>init :返回最后一个成员以外的所有成员组成的新数组。</p>    <pre>  <code class="language-javascript">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('')     // ''</code></pre>    <p>nth :取出指定位置的成员。</p>    <pre>  <code class="language-javascript">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') // ''</code></pre>    <p>take :取出前 n 个成员。</p>    <pre>  <code class="language-javascript">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'</code></pre>    <p>takeLast :取出后 n 个成员。</p>    <pre>  <code class="language-javascript">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'</code></pre>    <p>slice :从起始位置(包括)开始,到结束位置(不包括)为止,从原数组截取出一个新数组。</p>    <pre>  <code class="language-javascript">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'</code></pre>    <p>remove :移除开始位置后的 n 个成员。</p>    <pre>  <code class="language-javascript">R.remove(2, 3)([1,2,3,4,5,6,7,8]) // [1,2,6,7,8]</code></pre>    <p>insert :在指定位置插入给定值。</p>    <pre>  <code class="language-javascript">R.insert(2, 'x')([1,2,3,4]) // [1,2,'x',3,4]</code></pre>    <p>insertAll :在指定位置,插入另一个数组的所有成员。</p>    <pre>  <code class="language-javascript">R.insertAll(2,['x','y','z'])([1,2,3,4]) // [1,2,'x','y','z',3,4]</code></pre>    <p>prepend :在数组头部插入一个成员</p>    <pre>  <code class="language-javascript">R.prepend('fee')(['fi', 'fo', 'fum'])  // ['fee', 'fi', 'fo', 'fum']</code></pre>    <p>append :在数组尾部追加新的成员。</p>    <pre>  <code class="language-javascript">R.append('tests')(['write', 'more']) // ['write', 'more', 'tests']  R.append('tests')([]) // ['tests']  R.append(['tests'])(['write', 'more']) // ['write', 'more', ['tests']]</code></pre>    <p>intersperse :在数组成员之间插入表示分隔的成员。</p>    <pre>  <code class="language-javascript">R.intersperse('n')(['ba', 'a', 'a'])  // ['ba', 'n', 'a', 'n', 'a']</code></pre>    <p>join :将数组合并成一个字符串,并在成员之间插入分隔符。</p>    <pre>  <code class="language-javascript">R.join('|')([1, 2, 3]) // '1|2|3'</code></pre>    <h3>6.3 数组的过滤</h3>    <p>filter :过滤出符合条件的成员。</p>    <pre>  <code class="language-javascript">var isEven = n => n % 2 === 0;  R.filter(isEven)([1, 2, 3, 4]) // [2, 4]</code></pre>    <p>reject :过滤出所有不满足条件的成员。</p>    <pre>  <code class="language-javascript">var isOdd = (n) => n % 2 === 1;  R.reject(isOdd)([1, 2, 3, 4]) // [2, 4]</code></pre>    <p>takeWhile : 一旦满足条件,后面的成员都会被过滤。</p>    <pre>  <code class="language-javascript">var isNotFour = x => x !== 4;  R.takeWhile(isNotFour)([1, 2, 3, 4, 3, 2, 1]) // [1, 2, 3]</code></pre>    <p>dropWhile :一旦不满足条件,取出剩余的所有成员。</p>    <pre>  <code class="language-javascript">var lteTwo = x => x <= 2;  R.dropWhile(lteTwo)([1, 2, 3, 4, 3, 2, 1])  // [3, 4, 3, 2, 1]</code></pre>    <p>without :返回指定值以外的成员。</p>    <pre>  <code class="language-javascript">R.without([1, 2])([1, 2, 1, 3, 4])  // [3, 4]</code></pre>    <h3>6.4 单数组运算</h3>    <p>countBy :对每个成员执行指定函数以后,返回一个对象,表示各种执行结果分别包含多少成员。</p>    <pre>  <code class="language-javascript">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}</code></pre>    <p>splitAt :在给定位置,将原数组分成两个部分。</p>    <pre>  <code class="language-javascript">R.splitAt(1)([1, 2, 3]) // [[1], [2, 3]]  R.splitAt(5)('hello world') // ['hello', ' world']  R.splitAt(-1)('foobar') // ['fooba', 'r']</code></pre>    <p>splitEvery :按照指定的个数,将原数组分成多个部分。</p>    <pre>  <code class="language-javascript">R.splitEvery(3)([1, 2, 3, 4, 5, 6, 7])  // [[1, 2, 3], [4, 5, 6], [7]]    R.splitEvery(3)('foobarbaz')  // ['foo', 'bar', 'baz']</code></pre>    <p>splitWhen :以第一个满足指定函数的成员为界,将数组分成两个部分。</p>    <pre>  <code class="language-javascript">R.splitWhen(R.equals(2))([1, 2, 3, 1, 2, 3])  // [[1], [2, 3, 1, 2, 3]]</code></pre>    <p>aperture :每个成员与其后给定数量的成员分成一组,这些组构成一个新的数组。</p>    <pre>  <code class="language-javascript">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]]</code></pre>    <p>partition :根据是否满足指定函数,将成员分区。</p>    <pre>  <code class="language-javascript">R.partition(R.contains('s'))(['sss', 'ttt', 'foo', 'bars'])  // => [ [ 'sss', 'bars' ],  [ 'ttt', 'foo' ] ]</code></pre>    <p>indexOf :某个值在数组中第一次出现的位置。</p>    <pre>  <code class="language-javascript">R.indexOf(3)([1,2,3,4]) // 2  R.indexOf(10)([1,2,3,4]) // -1</code></pre>    <p>lastIndexOf :某个值在数组中最后一次出现的位置。</p>    <pre>  <code class="language-javascript">R.lastIndexOf(3)([-1,3,3,0,1,2,3,4]) // 6  R.lastIndexOf(10)([1,2,3,4]) // -1</code></pre>    <p>map :数组的每个成员依次执行某个函数。</p>    <pre>  <code class="language-javascript">var double = x => x * 2;  R.map(double)([1, 2, 3]) // [2, 4, 6]</code></pre>    <p>mapIndexed :与 map 类似,区别是遍历函数可以额外获得两个参数:索引位置和原数组。</p>    <pre>  <code class="language-javascript">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']</code></pre>    <p>forEach :数组的每个成员依次执行某个函数,总是返回原数组。</p>    <pre>  <code class="language-javascript">var printXPlusFive = x => console.log(x + 5);  R.forEach(printXPlusFive, [1, 2, 3]) // [1, 2, 3]  // logs 6  // logs 7  // logs 8</code></pre>    <p>reduce :数组成员依次执行指定函数,每一次的运算结果都会进入一个累积变量。</p>    <pre>  <code class="language-javascript">var mySubtract = function (a, b) {    return a - b;  };  R.reduce(mySubtract, 0)([1, 2, 3, 4]) // -10</code></pre>    <p>reduceRight :与 reduce 类似,区别是数组成员从左到右执行。</p>    <pre>  <code class="language-javascript">R.reduceRight(R.subtract, 0)([1, 2, 3, 4]) // -2</code></pre>    <p>reduceWhile :与 reduce 类似,区别是有一个判断函数,一旦数组成员不符合条件,就停止累积。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>sort :按照给定函数,对数组进行排序。</p>    <pre>  <code class="language-javascript">var diff = function(a, b) { return a - b; };  R.sort(diff)([4,2,7,5])  // [2, 4, 5, 7]</code></pre>    <p>sortWith :按照给定的一组函数,进行多重排序。</p>    <pre>  <code class="language-javascript">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]</code></pre>    <p>adjust :对指定位置的成员执行给定的函数。</p>    <pre>  <code class="language-javascript">R.adjust(R.add(10), 1)([1, 2, 3]) // [1, 12, 3]  R.adjust(R.add(10),1)([1, 2, 3]) // [1, 12, 3]</code></pre>    <p>ap :数组成员分别执行一组函数,将结果合成为一个新数组。</p>    <pre>  <code class="language-javascript">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"]</code></pre>    <p>flatten :将嵌套数组铺平。</p>    <pre>  <code class="language-javascript">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]</code></pre>    <p>groupBy :将数组成员依次按照指定条件两两比较,并按照结果将所有成员放入子数组。</p>    <pre>  <code class="language-javascript">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']</code></pre>    <h3>6.5 双数组运算</h3>    <p>concat :将两个数组合并成一个数组。</p>    <pre>  <code class="language-javascript">R.concat('ABC')('DEF') // 'ABCDEF'  R.concat([4, 5, 6])([1, 2, 3]) // [4, 5, 6, 1, 2, 3]  R.concat([])([]) // []</code></pre>    <p>zip :将两个数组指定位置的成员放在一起,生成一个新数组。</p>    <pre>  <code class="language-javascript">R.zip([1, 2, 3])(['a', 'b', 'c'])  // [[1, 'a'], [2, 'b'], [3, 'c']]</code></pre>    <p>zipObj :将两个数组指定位置的成员分别作为键名和键值,生成一个新对象。</p>    <pre>  <code class="language-javascript">R.zipObj(['a', 'b', 'c'])([1, 2, 3])  // {a: 1, b: 2, c: 3}</code></pre>    <p>xprod :将两个数组的成员两两混合,生成一个新数组。</p>    <pre>  <code class="language-javascript">R.xprod([1, 2])(['a', 'b'])  // [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]</code></pre>    <p>intersection :返回两个数组相同的成员组成的新数组。</p>    <pre>  <code class="language-javascript">R.intersection([1,2,3,4], [7,6,5,4,3]) // [4, 3]</code></pre>    <p>intersectionWith :返回经过某种运算,有相同结果的两个成员。</p>    <pre>  <code class="language-javascript">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'}]</code></pre>    <p>difference :返回第一个数组不包含在第二个数组里面的成员。</p>    <pre>  <code class="language-javascript">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}]</code></pre>    <p>differenceWith :返回执行指定函数后,第一个数组里面不符合条件的所有成员。</p>    <pre>  <code class="language-javascript">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}]</code></pre>    <p>symmetricDifference :返回两个数组的非共有成员所组成的一个新数组。</p>    <pre>  <code class="language-javascript">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]</code></pre>    <p>symmetricDifferenceWith :根据指定条件,返回两个数组所有运算结果不相等的成员所组成的新数组。</p>    <pre>  <code class="language-javascript">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}]</code></pre>    <h3>6.6 复合数组</h3>    <p>find :返回符合指定条件的成员。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>findIndex :返回符合指定条件的成员的位置。</p>    <pre>  <code class="language-javascript">var xs = [{a: 1}, {a: 2}, {a: 3}];  R.findIndex(R.propEq('a', 2))(xs) // 1  R.findIndex(R.propEq('a', 4))(xs) // -1</code></pre>    <p>findLast :返回最后一个符合指定条件的成员。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>findLastIndex :返回最后一个符合指定条件的成员的位置。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>pluck :取出数组成员的某个属性,组成一个新数组。</p>    <pre>  <code class="language-javascript">R.pluck('a')([{a: 1}, {a: 2}]) // [1, 2]  R.pluck(0)([[1, 2], [3, 4]])   // [1, 3]</code></pre>    <p>project :取出数组成员的多个属性,组成一个新数组。</p>    <pre>  <code class="language-javascript">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}]</code></pre>    <p>transpose :将每个成员相同位置的值,组成一个新数组。</p>    <pre>  <code class="language-javascript">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]]</code></pre>    <p>mergeAll :将数组的成员合并成一个对象。</p>    <pre>  <code class="language-javascript">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}</code></pre>    <p>fromPairs :将嵌套数组转为一个对象。</p>    <pre>  <code class="language-javascript">R.fromPairs([['a', 1], ['b', 2], ['c', 3]])  // {a: 1, b: 2, c: 3}</code></pre>    <p>groupBy :将数组成员按照指定条件分组。</p>    <pre>  <code class="language-javascript">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}]  // }</code></pre>    <p>sortBy :根据成员的某个属性排序。</p>    <pre>  <code class="language-javascript">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]</code></pre>    <h2>七、对象</h2>    <h3>7.1 对象的特征判断</h3>    <p>has : 返回一个布尔值,表示对象自身是否具有该属性。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>hasIn :返回一个布尔值,表示对象自身或原型链上是否具有某个属性。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>propEq :如果属性等于给定值,返回 true 。</p>    <pre>  <code class="language-javascript">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]</code></pre>    <p>whereEq :如果属性等于给定值,返回 true 。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>where :如果各个属性都符合指定条件,返回 true 。</p>    <pre>  <code class="language-javascript">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</code></pre>    <h3>7.2 对象的过滤</h3>    <p>omit :过滤指定属性。</p>    <pre>  <code class="language-javascript">R.omit(['a', 'd'])({a: 1, b: 2, c: 3, d: 4})  // {b: 2, c: 3}</code></pre>    <p>filter :返回所有满足条件的属性</p>    <pre>  <code class="language-javascript">var isEven = n => n % 2 === 0;  R.filter(isEven)({a: 1, b: 2, c: 3, d: 4}) // {b: 2, d: 4}</code></pre>    <p>reject :返回所有不满足条件的属性</p>    <pre>  <code class="language-javascript">var isOdd = (n) => n % 2 === 1;  R.reject(isOdd)({a: 1, b: 2, c: 3, d: 4})  // {b: 2, d: 4}</code></pre>    <h3>7.3 对象的截取</h3>    <p>dissoc :过滤指定属性。</p>    <pre>  <code class="language-javascript">R.dissoc('b')({a: 1, b: 2, c: 3})  // {a: 1, c: 3}</code></pre>    <p>assoc :添加或改写某个属性。</p>    <pre>  <code class="language-javascript">R.assoc('c', 3)({a: 1, b: 2})  // {a: 1, b: 2, c: 3}</code></pre>    <p>partition :根据属性值是否满足给定条件,将属性分区。</p>    <pre>  <code class="language-javascript">R.partition(R.contains('s'))({ a: 'sss', b: 'ttt', foo: 'bars' })  // [ { a: 'sss', foo: 'bars' }, { b: 'ttt' }  ]</code></pre>    <p>pick :返回指定属性组成的新对象</p>    <pre>  <code class="language-javascript">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}</code></pre>    <p>pickAll :与 pick 类似,但会包括不存在的属性。</p>    <pre>  <code class="language-javascript">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}</code></pre>    <p>pickBy :返回符合条件的属性</p>    <pre>  <code class="language-javascript">var isUpperCase = (val, key) => key.toUpperCase() === key;  R.pickBy(isUpperCase)({a: 1, b: 2, A: 3, B: 4})  // {A: 3, B: 4}</code></pre>    <p>keys :返回对象自身属性的属性名组成的新数组。</p>    <pre>  <code class="language-javascript">R.keys({a: 1, b: 2, c: 3}) // ['a', 'b', 'c']</code></pre>    <p>keysIn :返回对象自身的和继承的属性的属性名组成的新数组。</p>    <pre>  <code class="language-javascript">var F = function() { this.x = 'X'; };  F.prototype.y = 'Y';  var f = new F();  R.keysIn(f) // ['x', 'y']</code></pre>    <p>values :返回对象自身的属性的属性值组成的数组。</p>    <pre>  <code class="language-javascript">R.values({a: 1, b: 2, c: 3}); //=> [1, 2, 3]</code></pre>    <p>valuesIn :返回对象自身的和继承的属性的属性值组成的数组。</p>    <pre>  <code class="language-javascript">var F = function() { this.x = 'X'; };  F.prototype.y = 'Y';  var f = new F();  R.valuesIn(f) // ['X', 'Y']</code></pre>    <p>invertObj :将属性值和属性名互换。如果多个属性的属性值相同,只返回最后一个属性。</p>    <pre>  <code class="language-javascript">var raceResultsByFirstName = {    first: 'alice',    second: 'jake',    third: 'alice',  };  R.invertObj(raceResultsByFirstName)  // {"alice": "third", "jake": "second"}</code></pre>    <p>invert :将属性值和属性名互换,每个属性值对应一个数组。</p>    <pre>  <code class="language-javascript">var raceResultsByFirstName = {    first: 'alice',    second: 'jake',    third: 'alice',  };  R.invert(raceResultsByFirstName)  // { 'alice': ['first', 'third'], 'jake':['second'] }</code></pre>    <h3>7.4 对象的运算</h3>    <p>prop :返回对象的指定属性</p>    <pre>  <code class="language-javascript">R.prop('x')({x: 100})  // 100    R.prop('x')({})  // undefined</code></pre>    <p>map :对象的所有属性依次执行某个函数。</p>    <pre>  <code class="language-javascript">var double = x => x * 2;  R.map(double)({x: 1, y: 2, z: 3})  // {x: 2, y: 4, z: 6}</code></pre>    <p>mapObjIndexed :与 map 类似,但是会额外传入属性名和整个对象。</p>    <pre>  <code class="language-javascript">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' }</code></pre>    <p>forEachObjIndexed :每个属性依次执行给定函数,给定函数的参数分别是属性值和属性名,返回原对象。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>merge :合并两个对象,如果有同名属性,后面的值会覆盖掉前面的值。</p>    <pre>  <code class="language-javascript">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}</code></pre>    <p>mergeWith :合并两个对象,如果有同名属性,会使用指定的函数处理。</p>    <pre>  <code class="language-javascript">R.mergeWith(    R.concat,    { a: true, values: [10, 20] },    { b: true, values: [15, 35] }  );  // { a: true, b: true, values: [10, 20, 15, 35] }</code></pre>    <p>eqProps :比较两个对象的指定属性是否相等。</p>    <pre>  <code class="language-javascript">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</code></pre>    <p>R.evolve :对象的属性分别经过一组函数的处理,返回一个新对象。</p>    <pre>  <code class="language-javascript">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  // }</code></pre>    <h3>7.5 复合对象</h3>    <p>path :取出数组中指定路径的值。</p>    <pre>  <code class="language-javascript">R.path(['a', 'b'], {a: {b: 2}}) // 2  R.path(['a', 'b'], {c: {b: 2}}) // undefined</code></pre>    <p>pathEq :返回指定路径的值符合条件的成员</p>    <pre>  <code class="language-javascript">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 ]</code></pre>    <p>assocPath :添加或改写指定路径的属性的值。</p>    <pre>  <code class="language-javascript">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}}}</code></pre>    <p>(完)</p>    <p> </p>    <p>来自:http://www.ruanyifeng.com/blog/2017/03/ramda.html</p>    <p> </p>