diff --git a/src/index.d.ts b/src/index.d.ts index c466759cf9..5708015113 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1093,6 +1093,16 @@ declare namespace RamdaAdjunct { */ async(generatorFn: Function): Function; + /** + * Replace all substring matches in a string with a replacement. + */ + replaceAll(searchValue: string, replaceValue: string, str: string): string; + replaceAll(searchValue: string): (replaceValue: string, str: string) => string; + replaceAll(searchValue: string): { + (replaceValue: string, str: string): string; + (replaceValue: string): (str: string) => string; + }; + /** * Identity type. */ diff --git a/src/index.js b/src/index.js index c3beb924d0..d651e4352d 100644 --- a/src/index.js +++ b/src/index.js @@ -169,5 +169,7 @@ export { default as ceil } from './ceil'; export { default as floor } from './floor'; export { default as trunc } from './trunc'; export { default as sign } from './sign'; +// String +export { default as replaceAll } from './replaceAll'; // Types export { default as Identity } from './fantasy-land/Identity'; diff --git a/src/replaceAll.js b/src/replaceAll.js new file mode 100644 index 0000000000..5efb47a31d --- /dev/null +++ b/src/replaceAll.js @@ -0,0 +1,34 @@ +import { curryN, split, join, pipe } from 'ramda'; + +import isRegExp from './isRegExp'; + +/** + * Replaces all substring matches in a string with a replacement. + * + * @func replaceAll + * @memberOf RA + * @since {@link https://char0n.github.io/ramda-adjunct/2.17.0|v2.17.0} + * @category String + * @sig String -> String -> String -> String + * @param {string} searchValue The substring to match + * @param {string} replaceValue The string to replace the matches with + * @param {string} str The String to do the search and replacement in + * @return {string} A new string containing all the `searchValue` replaced with the `replaceValue` + * @throws {Error} When `searchValue` is RegExp + * @see {@link http://ramdajs.com/docs/#replace|R.replace}, {@link https://github.com/tc39/proposal-string-replaceall|TC39 proposal} + * @example + * + * RA.replaceAll('ac', 'ef', 'ac ab ac ab'); //=> 'ef ab ef ab' + */ +const replaceAll = curryN(3, (searchValue, replaceValue, str) => { + if (isRegExp(searchValue)) { + throw new Error('searchValue must be a String, not a RegExp.'); + } + + return pipe( + split(String(searchValue)), + join(String(replaceValue)) + )(str); +}); + +export default replaceAll; diff --git a/test/replaceAll.js b/test/replaceAll.js new file mode 100644 index 0000000000..e50e809524 --- /dev/null +++ b/test/replaceAll.js @@ -0,0 +1,50 @@ +import { assert } from 'chai'; + +import * as RA from '../src'; + +describe('replaceAll', function() { + it('should replace all matches', function() { + const value = 'ab cd ab cd ab cd'; + const actual = RA.replaceAll('ab', 'ef', value); + const expected = 'ef cd ef cd ef cd'; + + assert.strictEqual(actual, expected); + }); + + context('given searchValue is RegExp', function() { + specify('should throw Error', function() { + assert.throws(() => RA.replaceAll(/a/g, 'c', 'abc')); + }); + }); + + context('given empty string', function() { + specify('should return original value', function() { + assert.strictEqual(RA.replaceAll('a', 'c', ''), ''); + }); + }); + + it('should support boxing wrappers', function() { + const value = new String('ab cd ab cd ab cd'); + const searchValue = new String('ab'); + const replaceValue = new String('ef'); + const actual = RA.replaceAll(searchValue, replaceValue, value); + const expected = 'ef cd ef cd ef cd'; + + assert.strictEqual(actual, expected); + }); + + it('should replace in very big strings', function() { + const bigString = 'ab cd'.repeat(10000); + const expected = 'ab ef'.repeat(10000); + const actual = RA.replaceAll('cd', 'ef', bigString); + + assert.strictEqual(actual, expected); + }); + + it('should curry', function() { + assert.strictEqual(RA.replaceAll('a', 'c', 'aba'), 'cbc'); + assert.strictEqual(RA.replaceAll('a')('c', 'aba'), 'cbc'); + assert.strictEqual(RA.replaceAll('a', 'c')('aba'), 'cbc'); + assert.strictEqual(RA.replaceAll('a')('c')('aba'), 'cbc'); + }); +});