@@ -19,12 +19,12 @@ const assert = require('assert');
1919const { inspect } = require ( 'util' ) ;
2020const { curry, pipe } = require ( './functional' ) ;
2121const {
22- plus, or, mul, and,
22+ plus, or, mul, and, is ,
2323} = require ( './op' ) ;
2424const { type } = require ( './typesafe' ) ;
2525const { Trait } = require ( './trait' ) ;
2626const {
27- size, Size, pairs, eq, empty, _typedArrays,
27+ size, Size, pairs, eq, empty, _typedArrays, setdefault ,
2828} = require ( './stdtraits' ) ;
2929
3030// ITERATOR GENERATORS ///////////////////////////////////////
@@ -1249,6 +1249,18 @@ const takeWhile = curry('takeWhile', function* takeWhile(seq, fn) {
12491249 }
12501250} ) ;
12511251
1252+ /**
1253+ * Cut off the sequence at the first point where the given condition is met.
1254+ *
1255+ * `list(takeUntil([1,2,3,4,5,6...], x => x > 4))` yields `[1,2,3,4]`
1256+ *
1257+ * @function
1258+ * @param {Sequence } seq Any sequence for which iter() is defined
1259+ * @param {Function } fn The predicate function
1260+ * @returns {Iterator }
1261+ */
1262+ const takeUntil = curry ( 'takeUntil' , ( seq , fn ) => takeWhile ( seq , ( v ) => ! fn ( v ) ) ) ;
1263+
12521264/**
12531265 * Cut of the sequence at the point where the given value is
12541266 * first encountered.
@@ -1257,7 +1269,7 @@ const takeWhile = curry('takeWhile', function* takeWhile(seq, fn) {
12571269 * @param {Sequence } seq Any sequence for which iter() is defined
12581270 * @returns {Iterator }
12591271 */
1260- const takeUntilVal = curry ( 'takeUntilVal' , ( seq , val ) => takeWhile ( seq , ( x ) => x !== val ) ) ;
1272+ const takeUntilVal = curry ( 'takeUntilVal' , ( seq , val ) => takeUntil ( seq , is ( val ) ) ) ;
12611273
12621274/**
12631275 * Cut of the given sequence at the first undefined or null value.
@@ -1659,6 +1671,64 @@ const chunkifyWithFallback = curry('chunkifyWithFallback', (seq, len, fallback)
16591671 } ) ,
16601672) ) ;
16611673
1674+ /**
1675+ * Group the elements of the user defined sequence using a custom container.
1676+ *
1677+ * This will:
1678+ *
1679+ * - Calculate the key for every element in the given sequence by
1680+ * applying the key function
1681+ * - Create a bucket (array) for every key calculated
1682+ * - Insert each element into the bucket associated with
1683+ * it's calculated key in order
1684+ *
1685+ * ```js,test
1686+ * const { group, assertEquals } = require('ferrum');
1687+ *
1688+ * const seq = [
1689+ * { foo: 42, bar: 22 },
1690+ * { foo: 13, bar: 22 },
1691+ * { foo: 42, bar: 99 },
1692+ * ];
1693+ *
1694+ * // Group by `foo`
1695+ * assertEquals(
1696+ * group(seq, ({foo}) => foo),
1697+ * new Map([
1698+ * [42, [
1699+ * { foo: 42, bar: 22 }, // Note that the order in here is well defined
1700+ * { foo: 42, bar: 99 }]],
1701+ * [13, [
1702+ * { foo: 13, bar: 22 }]]
1703+ * ])
1704+ * );
1705+ *
1706+ * // Group by `bar`
1707+ * assertEquals(
1708+ * group(seq, ({bar}) => bar),
1709+ * new Map([
1710+ * [22, [
1711+ * { foo: 42, bar: 22 },
1712+ * { foo: 13, bar: 22 }]],
1713+ * [42, [
1714+ * { foo: 42, bar: 99 }]]
1715+ * ])
1716+ * );
1717+ * ```
1718+ *
1719+ * @function
1720+ * @sourcecode
1721+ * @param {Sequence } seq
1722+ * @param {Function } keyfn
1723+ * @returns {Map } The es6 map containing the keys.
1724+ */
1725+ const group = curry ( 'group' , ( seq , keyfn ) => {
1726+ const cont = new Map ( ) ;
1727+ each ( seq , ( elm ) => setdefault ( cont , keyfn ( elm ) , [ ] ) . push ( elm ) ) ;
1728+ return cont ;
1729+ } ) ;
1730+
1731+
16621732/**
16631733 * Calculate the cartesian product of the given sequences.
16641734 *
@@ -1831,6 +1901,7 @@ module.exports = {
18311901 take,
18321902 takeWithFallback,
18331903 takeWhile,
1904+ takeUntil,
18341905 takeUntilVal,
18351906 takeDef,
18361907 flat,
@@ -1851,6 +1922,7 @@ module.exports = {
18511922 chunkifyShort,
18521923 chunkify,
18531924 chunkifyWithFallback,
1925+ group,
18541926 cartesian,
18551927 cartesian2,
18561928 mod,
0 commit comments