Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: concatAll #404

Merged
merged 10 commits into from
Feb 24, 2018
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[];

/**
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to align this description with the one I proposed in previous comment

* 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);
});
});
});