Skip to content

Commit

Permalink
feat: add concatAll (#404)
Browse files Browse the repository at this point in the history
Ref #109
  • Loading branch information
wojpawlik authored and char0n committed Feb 24, 2018
1 parent 2f20beb commit 341f3f1
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/concatAll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { concat, identical, identity, pipe, when, reduce } from 'ramda';

import stubUndefined from './stubUndefined';

const leftIdentitySemigroup = { concat: identity };

/**
* Returns the result of concatenating the given lists or strings.
* Note: RA.concatAll expects all elements to be of the same type. It will throw an error if you concat an Array with a non-Array value.
* Dispatches to the concat method of the preceding element, if present. Can also concatenate multiple elements of a [fantasy-land compatible semigroup](https://github.com/fantasyland/fantasy-land#semigroup).
* Returns undefined if empty array was passed.
*
* @func concatAll
* @memberOf RA
* @since {@link https://char0n.github.io/ramda-adjunct/2.6.0|v2.6.0}
* @category List
* @sig [[a]] -> [a] | Undefined
* @sig [String] -> String | Undefined
* @sig Semigroup s => Foldable s f => f -> s | Undefined
* @param {Array.<Array|string>} list List containing elements that will be concatenated
* @return {Array|string|undefined} Concatenated elements
* @see {@link http://ramdajs.com/docs/#concat|concat}
* @see {@link RA.concatRight|concatRight}
* @see {@link http://ramdajs.com/docs/#unnest|unnest}
* @see {@link http://ramdajs.com/docs/#join|join}
* @example
*
* concatAll([[1], [2], [3]]); //=> [1, 2, 3]
* concatAll(['1', '2', '3']); //=> '123'
* concatAll([]); //=> undefined;
*/
const concatAll = pipe(
reduce(concat, leftIdentitySemigroup),
when(identical(leftIdentitySemigroup), stubUndefined)
);

export default concatAll;
17 changes: 17 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ declare namespace RamdaAdjunct {
ap<U>(fn: Apply<(t: T) => U>): Apply<U>;
}

interface Foldable<T> {
reduce<Acc>(fn: (acc: Acc, val: T) => Acc, initAcc: Acc): Acc;
}

interface Semigroup {
// https://www.typescriptlang.org/docs/handbook/advanced-types.html#polymorphic-this-types
concat(other: this): this;
}

interface Catamorphism<T> {
cata<T1>(leftFn: (v: T1) => T, rightFn: (v: T1) => T): T;
}
Expand Down Expand Up @@ -376,6 +385,14 @@ declare namespace RamdaAdjunct {
*/
ensureArray<T>(value: T | T[]): T[];

/**
* Returns the result of concatenating the given lists or strings.
* Note: RA.concatAll expects all elements to be of the same type. It will throw an error if you concat an Array with a non-Array value.
* Dispatches to the concat method of the preceding element, if present. Can also concatenate multiple elements of a [fantasy-land compatible semigroup](https://github.com/fantasyland/fantasy-land#semigroup).
* Returns undefined if empty array was passed.
*/
concatAll<S extends Semigroup>(foldable: Foldable<S>): S | undefined;

/**
* Returns the result of concatenating the given lists or strings.
*/
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export { default as reduceIndexed } from './reduceIndexed';
export { default as pickIndexes } from './pickIndexes';
export { default as list } from './list';
export { default as ensureArray } from './ensureArray';
export { default as concatAll } from './concatAll';
export { default as concatRight } from './concatRight';
export { default as reduceP } from './reduceP';
export { default as reduceRightP } from './reduceRightP';
Expand Down
46 changes: 46 additions & 0 deletions test/concatAll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { assert } from 'chai';
import { NEL, Nil } from 'monet';

import * as RA from '../src';
import eq from './shared/eq';

describe('concatAll', function() {
it('should concatenate arrays', function() {
eq(RA.concatAll([[1], [2], [3]]), [1, 2, 3]);
});

it('should concatenate strings', function() {
eq(RA.concatAll(['1', '2', '3']), '123');
});

it('should concatenate semigroups', function() {
eq(RA.concatAll([NEL(1), NEL(2)]), NEL(1, NEL(2, Nil)));
});

context('when foldable is empty', function() {
specify('should returns undefined', function() {
eq(RA.concatAll([]), undefined);
});
});

context('when foldable contains non-semigroups', function() {
specify('should throw', function() {
assert.throws(() => RA.concatAll([1, 2, null, true]), TypeError);
});
});

/*
// fails on Ramda 0.21
context('when foldable contains non-compatible semigroups', function() {
specify('should throw', function() {
assert.throws(() => RA.concatAll([[1], '1']), TypeError);
});
});
*/

context('when passed non-foldable', function() {
specify('should throw', function() {
assert.throws(() => RA.concatAll(null), TypeError);
});
});
});

0 comments on commit 341f3f1

Please sign in to comment.