Skip to content

Commit b919d1a

Browse files
author
Ovidiu Barabula
committed
feat(util): add limitFn and range utility functions
1 parent 10797a4 commit b919d1a

File tree

2 files changed

+172
-1
lines changed

2 files changed

+172
-1
lines changed

src/util/utility-functions.spec.ts

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { assert, expect } from 'chai';
22
import 'mocha';
3-
import { hasNested } from './utility-functions';
3+
import { ERRORS, hasNested, limitFn, range } from './utility-functions';
44

55
describe('Utility Functions', () => {
66
describe('hasNested', () => {
@@ -49,4 +49,104 @@ describe('Utility Functions', () => {
4949
expect(hasNested({}, '')).to.be.false;
5050
});
5151
});
52+
53+
54+
describe('limitFn', () => {
55+
it('returns a function', () => {
56+
const fn = () => true;
57+
expect(limitFn(fn)).to.be.a('function');
58+
});
59+
60+
61+
it('returns original function\'s return value', () => {
62+
const fn = () => true;
63+
expect(limitFn(fn)()).to.be.true;
64+
});
65+
66+
67+
it('limits the number of calls a function can take', () => {
68+
const fn = () => true;
69+
const limited = limitFn(fn);
70+
limited();
71+
expect(limited()).to.be.undefined;
72+
});
73+
74+
75+
it('takes custom limit argument', () => {
76+
const fn = () => true;
77+
const limited = limitFn(fn, 5);
78+
for (const i of range(1, 5)) {
79+
expect(limited()).to.be.true;
80+
}
81+
expect(limited()).to.be.undefined;
82+
});
83+
84+
85+
it('passes the function arguments to the limited limited function', () => {
86+
const add = (x: number, y: number) => x + y;
87+
const limited = limitFn(add);
88+
expect(limited(1, 3)).to.equal(4);
89+
expect(limited(2, 2)).to.be.undefined;
90+
});
91+
92+
93+
it('throws if passed limit argument is not a number', () => {
94+
const fn = () => true;
95+
assert.throws(() => limitFn(fn, '1'), ERRORS.LIMIT_NOT_A_NUMBER);
96+
});
97+
98+
99+
it('throws if argument is not a function', () => {
100+
assert.throws(() => limitFn(1), ERRORS.NOT_A_FUNCTION);
101+
});
102+
});
103+
104+
105+
describe('range', () => {
106+
it('creates an array from 1 to 5', () => {
107+
const subject = range(1, 5);
108+
expect(subject).to.be.an('array');
109+
expect(subject[0]).to.equal(1);
110+
expect(subject[subject.length - 1]).to.equal(5);
111+
});
112+
113+
114+
it('creates an array from -1 to 5', () => {
115+
const subject = range(-1, 5);
116+
expect(subject).to.be.an('array');
117+
expect(subject[0]).to.equal(-1);
118+
expect(subject[subject.length - 1]).to.equal(5);
119+
});
120+
121+
122+
it('creates an array from 5 to 1', () => {
123+
const subject = range(5, 1);
124+
expect(subject).to.be.an('array');
125+
expect(subject[0]).to.equal(5);
126+
expect(subject[subject.length - 1]).to.equal(1);
127+
});
128+
129+
130+
it('creates an array from -5 to 1', () => {
131+
const subject = range(-5, 1);
132+
expect(subject).to.be.an('array');
133+
expect(subject[0]).to.equal(-5);
134+
expect(subject[subject.length - 1]).to.equal(1);
135+
});
136+
137+
138+
it('throws if arguments are missing', () => {
139+
assert.throws(() => range(1), ERRORS.RANGE_NEEDS_TWO_PARAMS);
140+
});
141+
142+
143+
it('throws if arguments are not numbers', () => {
144+
assert.throws(() => range('1', '5'), ERRORS.RANGE_NEEDS_NUMBERS);
145+
});
146+
147+
148+
it('throws if limit arguments are equal', () => {
149+
assert.throws(() => range(2, 2), ERRORS.RANGE_LIMITS_EQUAL);
150+
});
151+
});
52152
});

src/util/utility-functions.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ interface NestedObject {
99
[key: string]: any;
1010
}
1111

12+
type AnyFunction = (...args: any[]) => any;
13+
14+
15+
export const ERRORS = {
16+
LIMIT_NOT_A_NUMBER: 'Limit argument must be a number',
17+
NOT_A_FUNCTION: 'Passed argument is not a function',
18+
RANGE_LIMITS_EQUAL: 'Range limits cannot be equal',
19+
RANGE_NEEDS_NUMBERS: 'Range limit arguments must be numbers',
20+
RANGE_NEEDS_TWO_PARAMS: 'Range requires two number parameters',
21+
};
22+
1223

1324
/**
1425
* Check object for path
@@ -34,3 +45,63 @@ export function hasNested(object: NestedObject, path: string | string[]): boolea
3445
return false;
3546
}
3647
}
48+
49+
50+
/**
51+
* Limit a function's number of available calls
52+
* @param fn Any function
53+
* @param limit Maximum numbers of calls
54+
*/
55+
export function limitFn(fn: AnyFunction | void, limit: number = 1): AnyFunction {
56+
if (typeof fn !== 'function') {
57+
throw new Error(ERRORS.NOT_A_FUNCTION);
58+
}
59+
60+
if (typeof limit !== 'number') {
61+
throw new Error(ERRORS.LIMIT_NOT_A_NUMBER);
62+
}
63+
64+
return function (...args) {
65+
if (fn && limit > 0) {
66+
limit -= 1;
67+
return fn(...args);
68+
}
69+
fn = undefined;
70+
return undefined;
71+
};
72+
}
73+
74+
75+
/**
76+
* Create an array of numbers between passed in limits
77+
* @param from Starting number
78+
* @param to End number
79+
*/
80+
export function range(from: number, to: number): number[] {
81+
if (typeof to === 'undefined') {
82+
throw new Error(ERRORS.RANGE_NEEDS_TWO_PARAMS);
83+
}
84+
85+
if (typeof from !== 'number' || typeof to !== 'number') {
86+
throw new Error(ERRORS.RANGE_NEEDS_NUMBERS);
87+
}
88+
89+
if (from === to) {
90+
throw new Error(ERRORS.RANGE_LIMITS_EQUAL);
91+
}
92+
93+
const direction: number = from < to ? +1 : -1;
94+
// Swich the values if we need to go from right to left
95+
if (direction < 0) {
96+
[from, to] = [to, from];
97+
}
98+
99+
const array: number[] = [];
100+
for (let i = from; i <= to; i++) {
101+
array.push(i);
102+
}
103+
104+
// If right to left, reverse the array
105+
// This keeps the above loop the same in both cases
106+
return direction > 0 ? array : array.reverse();
107+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy