Skip to content

Commit f6447d0

Browse files
committed
feat: add mapDistribute function
This new function makes the developer's life easier while distributing values into arrays.
1 parent a6d038f commit f6447d0

File tree

8 files changed

+132
-13
lines changed

8 files changed

+132
-13
lines changed

docs/04-api.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,43 @@ currency(12.35).distribute(3); // => [4.12, 4.12, 4.11]
8181
currency(12.00).distribute(3); // => [4.00, 4.00, 4.00]
8282
```
8383

84+
### mapDistribute
85+
86+
`currency.mapDistribute( arrayToDistribute, callbackFn )`
87+
`currency.mapDistribute( arrayToDistribute, callbackFn, thisArg )`
88+
89+
Map an array element distributing the currency value with the array element. See [distribute](#distribute)
90+
91+
#### Parameters
92+
93+
##### `arrayToDistribute`
94+
The array to distribute the value to.
95+
96+
##### `callbackFn`
97+
A function to execute for each element in the array and its own distribution. Its return value is added as a single element in the new array. The function is called with the following arguments:
98+
99+
> `element`
100+
>     The current element being processed in the array.
101+
> `distributionElement`
102+
>     The current distribution element corresponding to the `element`.
103+
> `index`
104+
>     The index of the current element being processed in the array.
105+
> `array`
106+
>     The `arrayToDistribute` use on the whole distribution.
107+
108+
##### `thisArg` *optional*
109+
A value to use as `this` when executing `callbackFn`.
110+
111+
```javascript
112+
const paymentInstruments = ['CreditCard1', 'CreditCard2']
113+
const mapped = currency(2.75).mapDistribute(paymentInstruments, callbackFn)
114+
// => the callbackFn is called
115+
// first with CreditCard1 and 1.38
116+
// then CreditCard2 and 1.37
117+
118+
// and mapped would be the array of all the results of callbackFn
119+
```
120+
84121
### format
85122

86123
`currency.format([ function | options ])`
@@ -123,4 +160,4 @@ Returns the cent value of the currency.
123160
```js
124161
currency(123.45).cents(); // => 45
125162
currency("0.99").cents(); // => 99
126-
```
163+
```

readme.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,17 @@ currency(5.00).subtract(0.50); // 4.50
8989
currency(45.25).multiply(3); // 135.75
9090
currency(1.12).distribute(5); // [0.23, 0.23, 0.22, 0.22, 0.22]
9191
```
92+
]
93+
There's also a utility function to map and distribute over an array
94+
```javascript
95+
const paymentInstruments = ['CreditCard1', 'CreditCard2']
96+
const mapped = currency(2.75).mapDistribute(paymentInstruments, callbackFn)
97+
// => the callbackFn is called
98+
// first with CreditCard1 and 1.38
99+
// then CreditCard2 and 1.37
92100

101+
// and mapped would be the array of all the results of callbackFn
102+
```
93103
There's even a built in formatter that will automatically place comma delimiters in the right place.
94104

95105
```javascript

src/currency.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ declare module 'currency.js' {
2727
multiply(number: currency.Any): currency;
2828
divide(number: currency.Any): currency;
2929
distribute(count: number): Array<currency>;
30+
mapDistribute<T, U>(arrayToDistribute: Array<T>, callbackFn: (element: T, distributionElement: currency, index: number, array: Array<T>) => U, thisArg?: any): Array<U>;
31+
3032
dollars(): number;
3133
cents(): number;
3234
format(opts?: currency.Options | currency.Format): string;

src/currency.js

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,35 @@ function format(currency, settings) {
104104
.replace('#', dollars.replace(groups, '$1' + separator) + (cents ? decimal + cents : ''));
105105
}
106106

107+
/**
108+
* Takes the currency amount and distributes the values evenly. Any extra pennies
109+
* left over from the distribution will be stacked onto the first set of entries.
110+
* And apply the mapFunction for each array element and it owns distributed value.
111+
* @param {number} intValue
112+
* @param {number} precision
113+
* @param {object} settings
114+
* @param {number} count
115+
* @returns {array}
116+
*/
117+
function distribute(intValue, precision, settings, count) {
118+
let distribution = []
119+
, split = Math[intValue >= 0 ? 'floor' : 'ceil'](intValue / count)
120+
, pennies = Math.abs(intValue - (split * count))
121+
, precisionToUse = settings.fromCents ? 1 : precision;
122+
123+
for (; count !== 0; count--) {
124+
let item = currency(split / precisionToUse, settings);
125+
126+
// Add any left over pennies
127+
pennies-- > 0 && (item = item[intValue >= 0 ? 'add' : 'subtract'](1 / precision));
128+
129+
distribution.push(item);
130+
}
131+
132+
return distribution;
133+
}
134+
135+
107136
currency.prototype = {
108137

109138
/**
@@ -153,22 +182,31 @@ currency.prototype = {
153182
* @returns {array}
154183
*/
155184
distribute(count) {
156-
let { intValue, _precision, _settings } = this
157-
, distribution = []
158-
, split = Math[intValue >= 0 ? 'floor' : 'ceil'](intValue / count)
159-
, pennies = Math.abs(intValue - (split * count))
160-
, precision = _settings.fromCents ? 1 : _precision;
185+
const { intValue, _precision, _settings } = this;
186+
187+
return distribute(intValue, _precision, _settings, count)
188+
},
161189

162-
for (; count !== 0; count--) {
163-
let item = currency(split / precision, _settings);
190+
/**
191+
* Takes the currency amount and distributes the values evenly. Any extra pennies
192+
* left over from the distribution will be stacked onto the first set of entries.
193+
* And apply the mapFunction for each array element and it owns distributed value.
194+
* @param {array} array to distribute on
195+
* @param {callbackFn} callback function to apply while mapping
196+
* @param {thisArg} A value to use as this when executing callbackFn
197+
* @returns {array}
198+
*/
199+
mapDistribute(arrayToDistribute, callbackFn, thisArg) {
200+
const { intValue, _precision, _settings } = this;
164201

165-
// Add any left over pennies
166-
pennies-- > 0 && (item = item[intValue >= 0 ? 'add' : 'subtract'](1 / precision));
202+
const distribution = distribute(intValue, _precision, _settings, arrayToDistribute.length)
203+
const distributionResult = []
167204

168-
distribution.push(item);
205+
for (const [index, item] of distribution.entries()) {
206+
distributionResult.push(callbackFn.call(thisArg, arrayToDistribute[index], item, index, arrayToDistribute));
169207
}
170208

171-
return distribution;
209+
return distributionResult;
172210
},
173211

174212
/**

src/currency.js.flow

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ declare class currency {
2525
multiply(number: $currency$any): currency;
2626
divide(number: $currency$any): currency;
2727
distribute(count: number): Array<currency>;
28+
mapDistribute<T, U>(arrayToDistribute: Array<T>, callbackFn: (element: T, distributionElement: currency, index: number, array: Array<T>) => U, thisArg?: any): Array<U>;
29+
2830
dollars(): number;
2931
cents(): number;
3032
format(options?: $currency$opts | formatFunction): string;

test/test.flow.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ currencyInstance.divide(currencyInstance);
4949
// distribute
5050
let a1: Array<currency> = currencyInstance.distribute(4);
5151

52+
// mapDistribute
53+
let elements: Array<string> = ['a', 'b']
54+
let a2: Array<[string, currency]> = currencyInstance.mapDistribute(elements, (element, c) => [element, c]);
55+
5256
// dollars
5357
let d1: number = currencyInstance.dollars();
5458

test/test.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import test from 'ava';
2+
import { spy } from 'sinon';
23
import currency from '../dist/currency';
34

45
test('should be immutable', t => {
@@ -191,6 +192,27 @@ test('should create non-equal distribution with a negative penny', t => {
191192
t.is(total, -0.01, 'sum of values matches our original amount');
192193
});
193194

195+
test('should map distributing the value by the array length', t => {
196+
var arrayToDistribute = ['a', 'b', 'c']
197+
var justCopyTheArguments = (element, currencyElement, index, array) => [element, currencyElement, index, array]
198+
var result = currency(1).mapDistribute(arrayToDistribute, justCopyTheArguments)
199+
200+
t.deepEqual(result, [
201+
['a', currency(0.34), 0, arrayToDistribute],
202+
['b', currency(0.33), 1, arrayToDistribute],
203+
['c', currency(0.33), 2, arrayToDistribute],
204+
])
205+
})
206+
207+
test('should call the callbackFn with thisArg', t => {
208+
var arrayToDistribute = ['a']
209+
var spyToTestThisArg = spy()
210+
var thisArg = { just: 'for Reference' }
211+
currency(1).mapDistribute(arrayToDistribute, spyToTestThisArg, thisArg)
212+
213+
t.is(spyToTestThisArg.firstCall.thisValue, thisArg)
214+
})
215+
194216
test('should get dollar value', t => {
195217
var value = currency(1.23);
196218

@@ -555,4 +577,4 @@ test('should handle fractional cents', t => {
555577
var values = currency(1234.56, { fromCents: true });
556578
t.is(values.intValue, 1235);
557579
t.is(values.value, 12.35);
558-
});
580+
});

test/test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ currencyInstance.divide(currencyInstance);
5252
// distribute
5353
let a1: Array<currency> = currencyInstance.distribute(4);
5454

55+
// mapDistribute
56+
let elements: Array<string> = ['a', 'b']
57+
let a2: Array<[string, currency]> = currencyInstance.mapDistribute(elements, (element, c) => [element, c]);
58+
5559
// dollars
5660
let d1: number = currencyInstance.dollars();
5761

0 commit comments

Comments
 (0)