Skip to content

Commit

Permalink
feat: Update "all", "any" and "reduce" to allow uncurried call
Browse files Browse the repository at this point in the history
  • Loading branch information
andreidmt committed Aug 28, 2020
1 parent 6de88da commit afae8e5
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 88 deletions.
61 changes: 42 additions & 19 deletions src/all/all.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,59 @@
import { pipe } from "../pipe/pipe"
import { isMatch } from "../is-match/is-match"

const _all = (_fn, _source) => {
const source = Array.isArray(_source) ? _source : [_source]
const fn = Array.isArray(_fn) ? pipe(..._fn) : _fn

for (let i = 0, length = source.length; i < length; i++) {
if (fn(source[i]) !== true) {
return false
}
}

return true
}

/**
* Test if all elements of array satisfy function
* Test if all elements of array satisfy a function
*
* @tag Core
* @signature (fn: Function) => (source: Array): boolean
* @see {@link allWith}
* @see {@link isMatch}
* @see {@link any}
* @see {@link anyWith}
* @param {Fn|Fn[]} fn Test function called on each elements
* @param {Array} source Source array to iterate over
*
* @param {Function} fn Function that all elements need to satisfy
* @param {Array} source Source array
* @return {Boolean} True if all elements pass, otherwise false
*
* @return {boolean}
* @name all
* @tag Array
* @signature (fn: Function) => (source: Array): Boolean
* @signature (fn: Function, source: Array): Boolean
*
* @see {@link allWith}
* @see {@link any}
* @see {@link anyWith}
*
* @example
* all(isNumber)([1, 2, 3])
* // => true
* all(is)([1, "asd", null])
*
* all(is, [1, "asd", null])
* // => false
*/
const all = fn => source => {
for (let i = 0, length = source.length - 1; i <= length; i++) {
if (fn.call(null, source[i]) !== true) {
return false
}
export const all = (...params) => {
// @signature (fn) => (source)
if (params.length <= 1) {
return source => _all(params[0], source)
}

return true
// @signature (fn, source)
return _all(...params)
}

const allWith = subset => all(isMatch(subset))
export const allWith = (...params) => {
// @signature (subset) => (source)
if (params.length <= 1) {
return source => _all(isMatch(params[0]), source)
}

export { all, allWith }
// @signature (subset, source)
return _all(isMatch(params[0]), params[1])
}
15 changes: 11 additions & 4 deletions src/all/all.test.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import test from "tape"
import { all, allWith, is } from ".."
import { all, allWith, is, get } from ".."

const isEven = source => source % 2 === 0
const hasId = source => is(source.id)

test("all(With)", t => {
t.equal(all(isEven)([2, 6, 4]), true, "Check all number are even")
t.equal(all(isEven)([2, 6, 4]), true, "Check all number are even (curried)")

t.equal(all(isEven, [2, 6, 4]), true, "Check all number are even (uncurried)")

t.equal(
all(item => is(item.id))([{}, { id: 2 }, {}]),
false,
'Check all elements have "id" property'
)

t.equal(
all(hasId)([{}, { id: 2 }, {}]),
all([get("id"), is], [{}, { id: 2 }, {}]),
false,
'Check all elements have "id" property'
)
Expand Down
88 changes: 43 additions & 45 deletions src/any/any.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,59 @@
import { pipe } from "../pipe/pipe"
import { isMatch } from "../is-match/is-match"

const _any = (_fn, _source) => {
const source = Array.isArray(_source) ? _source : [_source]
const fn = Array.isArray(_fn) ? pipe(..._fn) : _fn

for (let i = 0, length = source.length; i < length; i++) {
if (fn(source[i]) === true) {
return true
}
}

return false
}

/**
* Test if at least one element of array satisfies function
* Test if at least one element of array satisfies a function
*
* @name any
* @tag Core
* @signature (fn: Function) => (source: Array): boolean
* @see {@link isMatch}
* @see {@link all}
* @see {@link allWith}
* @param {Fn|Fn[]} fn Test function called on each elements
* @param {Array} source Source array to iterate over
*
* @param {Function} fn Function to be satisfied
* @param {Array} source Input array
* @return {Boolean} True if at least one element passes, otherwise false
*
* @returns {boolean} True if at least one object passes, false otherwise
* @name any
* @tag Array
* @signature (fn: Function) => (source: Array): Boolean
* @signature (fn: Function, source: Array): Boolean
*
* @see {@link anyWith}
* @see {@link all}
* @see {@link allWith}
*
* @example
* any(isNumber)([1, "string", NaN])
* // => true
*
* any(is)([null])
* any([get("id"), is], [{title: ""}, {}])
* // => false
*/
const any = fn => source =>
(Array.isArray(source) ? source : [source]).some(element => {
const testResult = fn(element)
export const any = (...params) => {
// @signature (fn) => (source)
if (params.length <= 1) {
return source => _any(params[0], source)
}

return testResult && typeof testResult === "boolean"
})
// @signature (fn, source)
return _any(...params)
}

/**
* Test if object properties match any object in input array
*
* @name anyWith
* @tag Core
* @signature (subset: Object) => (source: Object[]): boolean
* @see {@link any}
* @see {@link isMatch}
* @see {@link all}
* @see {@link allWith}
*
* @param {Object} subset Set of properties that should match
* @param {Object[]} source Input array
*
* @returns {boolean} True if at least one object matches, false otherwise
*
* @example
* anyWith({
* id: isNumber,
* name: "lorem",
* })([
* { id: "uuid", name: "lorem" },
* { id: 2, name: "foo" },
* { id: 3, name: "lorem", foo: "bar" },
* ])
* // => true
*/
const anyWith = subset => any(isMatch(subset))
export const anyWith = (...params) => {
// @signature (subset) => (source)
if (params.length <= 1) {
return source => _any(isMatch(params[0]), source)
}

export { any, anyWith }
// @signature (subset, source)
return _any(isMatch(params[0]), params[1])
}
19 changes: 17 additions & 2 deletions src/any/any.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
import test from "tape"
import { any, anyWith } from ".."
import { get, any, anyWith } from ".."

const isNumber = source => Number.isFinite(source)

test("any(With)", t => {
t.equal(
any(isNumber)([1, "string", NaN]),
true,
"Check any element is number"
"Check any element is number (curried)"
)

t.equal(
any(isNumber, [1, "string", NaN]),
true,
"Check any element is number (uncurried)"
)

t.equal(
any(
[get("boolFlag"), item => item === true],
[null, "2", { boolFlag: true }]
),
true,
"Check any element has a field (uncurried, multiple functions)"
)

t.equal(any(isNumber)([null, "2", {}]), false, "Check any element is number")
Expand Down
24 changes: 7 additions & 17 deletions src/reduce/reduce.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { pipe } from "../pipe/pipe"

const _reduce = (fn, defaultAcc, _source) => {
const _reduce = (_fn, defaultAcc, _source) => {
let acc = defaultAcc
const source = Array.isArray(_source) ? _source : [_source]
const fn = Array.isArray(_fn) ? pipe(..._fn) : _fn

for (let i = 0, length = source.length; i < length; i++) {
acc = Array.isArray(fn)
? pipe(...fn)(acc, source[i], i, source)
: fn(acc, source[i], i, source)
acc = fn(acc, source[i], i, source)
}

return acc
Expand All @@ -23,6 +22,7 @@ const _reduce = (fn, defaultAcc, _source) => {
*
* @return {mixed}
*
* @name reduce
* @tag Array
* @signature (fn: Function, defaultAcc: mixed) => (source: Array): mixed
* @signature (fn: Function, defaultAcc: mixed, source: Array): mixed
Expand All @@ -34,21 +34,11 @@ const _reduce = (fn, defaultAcc, _source) => {
* // => 3
*/
export const reduce = (...params) => {
/*
* @signature (fn: Fn|Fn[], defaultAcc: mixed) => (source: []): mixed
*
* reduce(sum, 0)([1, 2])
* // => 3
*/
if (params.length < 3) {
// @signature (fn, defaultAcc) => (source)
if (params.length <= 2) {
return source => _reduce(params[0], params[1], source)
}

/*
* @signature (fn: Fn|Fn[], defaultAcc: mixed, source: []): mixed
*
* reduce(sum, 0, [1, 2])
* // => 3
*/
// @signature (fn, defaultAcc, source)
return _reduce(...params)
}
2 changes: 1 addition & 1 deletion src/reduce/reduce.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ test("reduce", t => {
)

t.deepEquals(
reduce((acc, next) => ({ ...acc, [next]: next }), {})([1, 2, 3]),
reduce((acc, next) => ({ ...acc, [next]: next }), {}, [1, 2, 3]),
{ 1: 1, 2: 2, 3: 3 },
"From array to object with default acc"
)
Expand Down

0 comments on commit afae8e5

Please sign in to comment.