From 470574fdadc8d389e1a5d59a943c2ede68bba73f Mon Sep 17 00:00:00 2001 From: Matei Adriel Date: Wed, 25 Dec 2019 16:09:18 +0200 Subject: [PATCH] typescript(option): feat: added the 'oneOf' helper Signed-off-by: prescientmoon --- typescript/option/src/helpers/external.ts | 1 + typescript/option/src/helpers/oneOf.test.ts | 55 +++++++++++++++++++++ typescript/option/src/helpers/oneOf.ts | 23 +++++++++ typescript/option/test/constants.ts | 3 ++ 4 files changed, 82 insertions(+) create mode 100644 typescript/option/src/helpers/oneOf.test.ts create mode 100644 typescript/option/src/helpers/oneOf.ts diff --git a/typescript/option/src/helpers/external.ts b/typescript/option/src/helpers/external.ts index 9b60b88..889f6de 100644 --- a/typescript/option/src/helpers/external.ts +++ b/typescript/option/src/helpers/external.ts @@ -16,6 +16,7 @@ export * from './isSome' export * from './iter' export * from './map' export * from './mapAsync' +export * from './oneOf' export * from './optionify' export * from './or' export * from './orLazy' diff --git a/typescript/option/src/helpers/oneOf.test.ts b/typescript/option/src/helpers/oneOf.test.ts new file mode 100644 index 0000000..d7e91ef --- /dev/null +++ b/typescript/option/src/helpers/oneOf.test.ts @@ -0,0 +1,55 @@ +import { constantly } from '@thi.ng/compose' +import { expect } from 'chai' +import { spy } from 'sinon' +import { x, alwaysSomeX } from '../../test/constants' +import { None, Some } from '../types' +import { oneOf } from './oneOf' + +const alwaysSome = (v: T) => constantly(Some(v)) + +describe('The oneOf helper', () => { + it('should return None on an empty array', () => { + // act + const result = oneOf(x, []) + + // assert + expect(result).to.equal(None) + }) + + it('should return the result of the first function which evaluates to Some', () => { + // arrange + const alwaysNone = constantly(None) + + // act + const head = oneOf(x, [alwaysSome('head'), alwaysNone]) + const middle = oneOf(x, [alwaysNone, alwaysSome('middle'), alwaysNone]) + const tail = oneOf(x, [alwaysNone, alwaysSome('tail')]) + + // assert + expect(head).to.equal(Some('head')) + expect(middle).to.equal(Some('middle')) + expect(tail).to.equal(Some('tail')) + }) + + it('should not evaluate any more functions after it found the result', () => { + // arrange + const func = spy(alwaysSomeX) + + // act + oneOf(x, [alwaysSomeX, func]) + + // assert + expect(func.called).to.be.false + }) + + it('should pass the provided input to the functions', () => { + // arrange + const func = spy(alwaysSomeX) + + // act + oneOf(x, [func]) + + // assert + expect(func.calledWith(x)).to.be.true + }) +}) diff --git a/typescript/option/src/helpers/oneOf.ts b/typescript/option/src/helpers/oneOf.ts new file mode 100644 index 0000000..99fb6a8 --- /dev/null +++ b/typescript/option/src/helpers/oneOf.ts @@ -0,0 +1,23 @@ +import { Binder } from '../internalTypes' +import { isSome } from './isSome' +import { None } from '../types' + +/** + * Try a list of functions against a value. + * Return the value of the first call that succeeds (aka returns Some). + * If no function retursn Some this will default to None. + * + * @param input The input to pass to the functions. + * @param functions Iterable of functions to try against the input. + */ +export const oneOf = (input: T, functions: Binder[]) => { + for (const func of functions) { + const result = func(input) + + if (isSome(result)) { + return result + } + } + + return None +} diff --git a/typescript/option/test/constants.ts b/typescript/option/test/constants.ts index 1ebdc08..e6f1b1b 100644 --- a/typescript/option/test/constants.ts +++ b/typescript/option/test/constants.ts @@ -1,3 +1,4 @@ +import { constantly } from '@thi.ng/compose' import { Some } from '../src' // general value to pass around @@ -5,3 +6,5 @@ export const x = Symbol('x') // same as x but for some export const someX = Some(x) + +export const alwaysSomeX = constantly(someX)