Skip to content

Commit

Permalink
Merge branch 'feature/group'
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh Bassett committed Apr 2, 2019
2 parents 550ea02 + 632097b commit 5e84542
Show file tree
Hide file tree
Showing 14 changed files with 150 additions and 80 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Unreleased

* Rename group -> chunk
* Rename groupBy -> chunkBy

## 2.0.0 (2019-03-07)

* Use Array.from to convert iterables to arrays
Expand Down
20 changes: 20 additions & 0 deletions src/chunk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import eq from './eq'
import chunkBy from './chunkBy'

/**
* Chunks the values in a list.
*
* This is a special case of the `chunkBy` function where the values are compared
* using the strict equality `===` operator.
*
* @function
* @param {Array|String} as The list.
* @returns {Array|String} A list that contains the values in the list of `as`
* chunked into sublists of equal values.
* @example
*
* import { chunk } from 'fkit'
* chunk([1, 2, 2, 3, 3, 3]) // [[1], [2, 2], [3, 3, 3]]
* chunk('Mississippi') // ['M', 'i', 'ss', 'i', 'ss', 'i', 'pp', 'i']
*/
export default chunkBy(eq)
19 changes: 19 additions & 0 deletions src/chunk.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import chunk from './chunk'

describe('chunk', () => {
it('handles an empty array', () => {
expect(chunk([])).toEqual([])
})

it('handles an empty string', () => {
expect(chunk('')).toEqual([])
})

it('handles an array', () => {
expect(chunk([1, 2, 2, 3, 3, 3])).toEqual([[1], [2, 2], [3, 3, 3]])
})

it('handles a string', () => {
expect(chunk('Mississippi')).toEqual(['M', 'i', 'ss', 'i', 'ss', 'i', 'pp', 'i'])
})
})
22 changes: 22 additions & 0 deletions src/chunkBy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import curry from './curry'
import chunkBy from './uncurried/chunkBy'

/**
* Chunks the values in a list using a comparator function.
*
* The comparator function `f` compares two values, `a` and `b`. If the values
* are both considered to be in the same chunk, then the comparator function
* should return `true`. Otherwise it should return `false`.
*
* @function
* @param {Function} c The comparator function.
* @param {Array|String} as The list.
* @returns {Array|String} A list that contains the values in the list of `as`
* chunked into sublists that satisfy the comparator function `c`.
* @example
*
* import { chunkBy } from 'fkit'
* chunkBy((a, b) => a === b, [1, 2, 2, 3, 3, 3]) // [[1], [2, 2], [3, 3, 3]]
* chunkBy((a, b) => a === b, 'Mississippi') // ['M', 'i', 'ss', 'i', 'ss', 'i', 'pp', 'i']
*/
export default curry(chunkBy)
27 changes: 27 additions & 0 deletions src/chunkBy.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import chunkBy from './chunkBy'

describe('chunkBy', () => {
const f = (a, b) => a === b

it('handles an empty array', () => {
expect(chunkBy(f, [])).toEqual([])
})

it('handles an empty string', () => {
expect(chunkBy(f, '')).toEqual([])
})

it('handles an array', () => {
expect(chunkBy(f, [1, 2, 2, 3, 3, 3])).toEqual([[1], [2, 2], [3, 3, 3]])
})

it('handles a string', () => {
expect(chunkBy(f, 'Mississippi')).toEqual(['M', 'i', 'ss', 'i', 'ss', 'i', 'pp', 'i'])
})

it('calls the comparator function', () => {
const spy = jest.fn()
chunkBy(spy, [1, 2])
expect(spy).toHaveBeenCalledWith(2, 1)
})
})
20 changes: 0 additions & 20 deletions src/group.js

This file was deleted.

19 changes: 0 additions & 19 deletions src/group.test.js

This file was deleted.

15 changes: 5 additions & 10 deletions src/groupBy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,16 @@ import curry from './curry'
import groupBy from './uncurried/groupBy'

/**
* Groups the values in a list using a comparator function.
*
* The comparator function `f` compares two values, `a` and `b`. If the values
* are both considered to be in the same group, then the comparator function
* should return `true`. Otherwise it should return `false`.
* Groups the values in a given list using a function or key path.
*
* @function
* @param {Function} c The comparator function.
* @param {Function|String} f The function or key path to group the values with.
* @param {Array|String} as The list.
* @returns {Array|String} A list that contains the values in the list of `as`
* grouped into sublists that satisfy the comparator function `c`.
* @returns {Array|String} A list that contains the values the grouped values.
* @example
*
* import { groupBy } from 'fkit'
* groupBy((a, b) => a === b, [1, 2, 2, 3, 3, 3]) // [[1], [2, 2], [3, 3, 3]]
* groupBy((a, b) => a === b, 'Mississippi') // ['M', 'i', 'ss', 'i', 'ss', 'i', 'pp', 'i']
* groupBy(Math.floor, [1.1, 2.2, 3.3]) // ['1': [1], '2': [2], '3': [3]]
* groupBy('length', ['one', 'two', 'three']) // ['3': ['one', 'two'], '5': ['three']]
*/
export default curry(groupBy)
19 changes: 9 additions & 10 deletions src/groupBy.test.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
import groupBy from './groupBy'
import id from './id'

describe('groupBy', () => {
const f = (a, b) => a === b

it('handles an empty array', () => {
expect(groupBy(f, [])).toEqual([])
expect(groupBy(id, [])).toEqual({})
})

it('handles an empty string', () => {
expect(groupBy(f, '')).toEqual([])
expect(groupBy(id, '')).toEqual({})
})

it('handles an array', () => {
expect(groupBy(f, [1, 2, 2, 3, 3, 3])).toEqual([[1], [2, 2], [3, 3, 3]])
expect(groupBy(id, [1, 2, 2, 3, 3, 3])).toEqual({ '1': [1], '2': [2, 2], '3': [3, 3, 3] })
})

it('handles a string', () => {
expect(groupBy(f, 'Mississippi')).toEqual(['M', 'i', 'ss', 'i', 'ss', 'i', 'pp', 'i'])
expect(groupBy(id, 'Mississippi')).toEqual({ 'M': ['M'], 'i': ['i', 'i', 'i', 'i'], p: ['p', 'p'], 's': ['s', 's', 's', 's'] })
})

it('calls the comparator function', () => {
const spy = jest.fn()
groupBy(spy, [1, 2])
expect(spy).toHaveBeenCalledWith(2, 1)
it('handles an grouping by a key path', () => {
const a = { address: { city: 'Melbourne' } }
const b = { address: { city: 'Sydney' } }
expect(groupBy('address.city', [a, b])).toEqual({ 'Melbourne': [a], 'Sydney': [b] })
})
})
6 changes: 3 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export { default as between } from './between'
export { default as binary } from './binary'
export { default as branch } from './branch'
export { default as cartesian } from './cartesian'
export { default as chunk } from './chunk'
export { default as chunkBy } from './chunkBy'
export { default as clamp } from './clamp'
export { default as compare } from './compare'
export { default as compose } from './compose'
Expand Down Expand Up @@ -44,8 +46,6 @@ export { default as fold } from './fold'
export { default as foldRight } from './foldRight'
export { default as get } from './get'
export { default as getIn } from './getIn'
export { default as group } from './group'
export { default as groupBy } from './groupBy'
export { default as gt } from './gt'
export { default as gte } from './gte'
export { default as head } from './head'
Expand Down Expand Up @@ -120,9 +120,9 @@ export { default as toUpper } from './toUpper'
export { default as tuple } from './tuple'
export { default as unary } from './unary'
export { default as uncurry } from './uncurry'
export { default as unzip } from './unzip'
export { default as union } from './union'
export { default as unionBy } from './unionBy'
export { default as unzip } from './unzip'
export { default as update } from './update'
export { default as values } from './values'
export { default as variadic } from './variadic'
Expand Down
8 changes: 8 additions & 0 deletions src/internal/isFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Returns true if `a` is a function.
*
* @private
*/
export default function isFunction (a) {
return (a instanceof Function)
}
6 changes: 4 additions & 2 deletions src/internal/isString.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* Returns true if `as` is a string or an array of strings.
* Returns true if `a` is a string.
*
* @private
*/
export default function isString (as) { return (typeof as === 'string') }
export default function isString (a) {
return (typeof a === 'string')
}
18 changes: 18 additions & 0 deletions src/uncurried/chunkBy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import empty from '../empty'
import head from '../head'
import last from '../last'
import span from '../span'
import tail from '../tail'
import prepend from './prepend'

export default function chunkBy (c, as) {
const b = head(as)
const bs = span(a => c(a, b), tail(as))

return empty(as)
? []
: prepend(
prepend(b, head(bs)),
chunkBy(c, last(bs))
)
}
26 changes: 10 additions & 16 deletions src/uncurried/groupBy.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import empty from '../empty'
import head from '../head'
import last from '../last'
import span from '../span'
import tail from '../tail'
import prepend from './prepend'
import fold from './fold'
import getIn from './getIn'
import isFunction from '../internal/isFunction'

export default function groupBy (c, as) {
const b = head(as)
const bs = span(a => c(a, b), tail(as))

return empty(as)
? []
: prepend(
prepend(b, head(bs)),
groupBy(c, last(bs))
)
export default function groupBy (f, as) {
const iteratee = a => isFunction(f) ? f(a) : getIn(f, a)
return fold((memo, a) => {
const key = iteratee(a);
(memo[key] = memo[key] || []).push(a)
return memo
}, {}, as)
}

0 comments on commit 5e84542

Please sign in to comment.