diff --git a/README.md b/README.md index 164d30b..e7cc743 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Browser Installation. The module is exported as *orbarr* global variable. ```html - + ``` Node Installation @@ -13,6 +13,10 @@ Node Installation npm install orb-array ``` +# Versions +## 2.0 +* **Breaking**: ***reduce.a*** is moved to ***map.a***. + # APIs ## split It splits an array into the specified number of pieces. When the number of pieces is larger than the input size, it creates empty pieces. It always returns the specified number of pieces. Some examples: @@ -66,7 +70,7 @@ const o = reduce.o(items, {value: v => v + 2}) // Output: {0:2, 1:3, 2:4, 3:5, 4:6} ``` -**reduce.mul** multiplies elements of the input array together. When the input contains a non-numerical value, the output is **NaN**. The boolean values are converted to their numerical form (0 or 1). +**reduce.mul** multiplies together all the elements of an array. When the input contains a non-numerical value, the output is **NaN**. The boolean values are converted to their numerical form (0 or 1). ```js const items = [1, 2, 5, 6] const result = reduce.mul(items) @@ -78,6 +82,22 @@ const result = reduce.mul([]) // Output: 1 ``` +**reduce.rollingmul** performs cumulative multiplication. Follow the following examples: +``` +// Rolling Multiplication +const items = [1, 2, 5, 6] +const o = reduce.rollingmul(items) +// Output: [1, 2, 10, 60] +``` + +**reduce.sum** adds the elements together. +``` +// Addition +const items = [1, 2, 3, 4, 6] +const o = reduce.sum(items) +// Output: 16 +``` + ## map *map* supports several operations. @@ -87,3 +107,75 @@ const items = range(5) const scaled = map.scale(items, 2) // Output: [0, 2, 4, 6, 8] ``` + +**map.a** transforms an array. A value function is applied to the original values. A custom container can be used to store the results. Some examples: +```js +// Simple Example +const items = range(5) +const o = map.a(items) +// Output: [0, 1, 2, 3, 4] +``` + +```js +// Using value function and a container +const items = [1, 2, 5, 6] +const vfn = v => 2*v +const container = [20] +const o = map.a(items, {value: vfn, container}) +// Output: [20, 2, 4, 10, 60] +``` + +## last +*last* gets the last element from an array without modification. +```js +// Action +const items = ['last-input'] +const o = last(items) +// Output: 'last-input' +``` + +## ranges +*ranges* simplifies nested iterations over the deep arrays. It creates a list of addresses for individual elements. **resolveAddress** API resolves them. Follow the following examples: +```js +// Ranges Demonstration +const o = ranges(2, 3) +// Output: [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]] +``` +```js +// Access Elements +const deepfruits = [['mango'], ['apple']] +const o = ranges(2, 2) +const fruits = o.map((address) => resolveAddress(deepfruits, address) +// Output: ['mango', 'apple'] +``` + +## resolveAddress +*resolveAddress* resolves the ordered indices in a nested array or the ordered keys in a nested object. The order must follow the parent-child relationship. +```js +// Array Example +const deepfruits = [['mango'], ['apple']] +const o = resolveAddress(deepfruits, [1, 0]) +// Output: 'apple' +``` +```js +// Object Example +const fruits = { + tropical: { + summer: ['mango', 'orange'] + }, + wild: { + names: ['berries'] + } +} +const o = resolveAddress(fruits, ['wild', 'names']) +// Output: ['berries] +``` + +## repeat +*repeat* repeats an object. The repeat count is configurable. +```js +// Basic Example +const input = {tree: 'tonmayi'} +const o = repeat(input, 2) +// Output: [{tree: 'tonmayi'}, {tree: 'tonmayi'}, {tree: 'tonmayi'}] +``` diff --git a/package-lock.json b/package-lock.json index 590f85a..7e95ad3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,14 @@ { "name": "orb-array", - "version": "1.1.0", + "version": "1.5.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "1.1.0", + "version": "1.5.0", "license": "MIT", "dependencies": { - "orb-functions": "^1.0.0" + "orb-functions": "^1.3.0" }, "devDependencies": { "ava": "^3.15.0", @@ -2627,9 +2627,9 @@ } }, "node_modules/orb-functions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/orb-functions/-/orb-functions-1.0.0.tgz", - "integrity": "sha512-L8KGrYVov57Mj7wljC4vluoKhDP7m8Zn59e+Q3MGQzT0xqlC3iq5xCVJXpfpzUyxLNGVLMsINv19Koej4/pydA==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/orb-functions/-/orb-functions-1.3.0.tgz", + "integrity": "sha512-brGUaddmf/9sEg8bjfSIgw6vs/IzIptLrINoJ1a6BJUWZjJ3TqYBxoxiyC6HrKvX3z7+CjJlfek4l4NGHG0lVg==" }, "node_modules/p-cancelable": { "version": "1.1.0", @@ -6209,9 +6209,9 @@ } }, "orb-functions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/orb-functions/-/orb-functions-1.0.0.tgz", - "integrity": "sha512-L8KGrYVov57Mj7wljC4vluoKhDP7m8Zn59e+Q3MGQzT0xqlC3iq5xCVJXpfpzUyxLNGVLMsINv19Koej4/pydA==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/orb-functions/-/orb-functions-1.3.0.tgz", + "integrity": "sha512-brGUaddmf/9sEg8bjfSIgw6vs/IzIptLrINoJ1a6BJUWZjJ3TqYBxoxiyC6HrKvX3z7+CjJlfek4l4NGHG0lVg==" }, "p-cancelable": { "version": "1.1.0", diff --git a/package.json b/package.json index 1dacb54..fab9062 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "orb-array", - "version": "1.1.0", - "description": "Less Verbose Array Usage", + "version": "2.0.0", + "description": "Concise Array Programming", "main": "./src/index.js", "files": [ "src/**/!(*.test.js)", @@ -23,8 +23,22 @@ }, "keywords": [ "array", - "lessverbose", - "functions" + "concise", + "fill", + "last", + "map", + "multiply", + "orb-array", + "range", + "reduce", + "rollingmultiply", + "scale", + "split", + "sum", + "ranges", + "repeat", + "resolveAddress", + "zip" ], "author": "NareshPS", "license": "MIT", @@ -33,7 +47,7 @@ }, "homepage": "https://github.com/NareshPS/orb-array#readme", "dependencies": { - "orb-functions": "^1.0.0" + "orb-functions": "^1.3.0" }, "devDependencies": { "ava": "^3.15.0", diff --git a/src/index.js b/src/index.js index 5512d1c..4add4d5 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -const { self } = require("orb-functions") +const { self, constant } = require("orb-functions") const get = a => a || [] @@ -24,11 +24,67 @@ const reduce = { o: (items = [], {key: kfn = self, value: vfn = self} = {}) => items.reduce((c /** container */, v, index) => (c[kfn(v, index)] = vfn(v, index), c), {}), - mul: (items = []) => items.reduce((v, vi) => v*vi, 1) + mul: (items = []) => items.reduce((v, vi) => v*vi, 1), + rollingmul: ([first, ...rest] = []) => rest.reduce( + (container, item) => ( + container.push(item*last(container)), + container + ), + first? [first]: [] + ), + sum: (items = []) => items.reduce((v, vi) => v+vi, 0) } const map = { - scale: (items = [], factor = 1) => items.map((item) => item*factor) + a: (items = [], {value: vfn = self, container = []} = {}) => + items.reduce((c /** container */, v, index) => (c.push(vfn(v, index)), c), container), + + scale: (items = [], factor = 1) => items.map((item) => item*factor), +} + +const ranges = (...ns /** range sizes */) => { + const cs = reduce.rollingmul(ns.reverse()).reverse() // range container sizes in decreasing order + const nes = cs.shift() // total number of elements. + const addressFn = (index) => cs.reduce( + ([rs /**remaining size */, address], csi) => { + return [rs%csi, (address.push(Math.floor(rs/csi)), address)] + }, + [index, []] + ) + + cs.push(1) // Deepest container size + return range(nes).map((index) => addressFn(index)[1]) } -module.exports = {split, range, reduce, map, fill, zip} \ No newline at end of file +const last = (array = []) => !(array instanceof Array)? array: array[array.length - 1] + +/** + * It resolves an address in an Array/Object + * + * @param {Array} value + * @param {Array} address + * @returns + */ +const resolveAddress = (value = [], address = []) => address.reduce((v, ai) => v[ai], value) + +/** + * Repeats the input object {count} times. + * + * @param {Object} o + * @param {number} count + * @returns + */ + const repeat = (o, count = 1) => fill(count + 1, constant(o)) + +module.exports = { + split, + range, + reduce, + map, + fill, + zip, + last, + ranges, + resolveAddress, + repeat +} \ No newline at end of file diff --git a/src/index.test.js b/src/index.test.js index 8d2beee..ed454b8 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -1,27 +1,28 @@ const test = require('ava') -const { split, range, fill, zip, reduce, map } = require('./index.js') +const functions = require('orb-functions') +const { split, range, fill, zip, reduce, map, ranges, last, resolveAddress, repeat } = require('./index.js') /////////////////////////// map.scale [start] ///////////////////////// -test('map-scale-no-args', t => { +test('map.scale-no-args', t => { const o = map.scale() t.deepEqual(o, []) }) -test('map-scale-empty-items', t => { +test('map.scale-empty-items', t => { const o = map.scale([]) t.deepEqual(o, []) }) -test('map-scale-list-no-scaling-factor', t => { +test('map.scale-list-no-scaling-factor', t => { const items = [1, 2, 5, 6] const o = map.scale(items) t.deepEqual(o, items) }) -test('map-scale-list-with-scaling-factor', t => { +test('map.scale-list-with-scaling-factor', t => { const items = [1, 2, 5, 6] const o = map.scale(items, 2) @@ -29,34 +30,67 @@ test('map-scale-list-with-scaling-factor', t => { }) /////////////////////////// map.scale [end] /////////////////////////// +/////////////////////////// map.a [start] /////////////////////////// +test('map.a-no-args', t => { + const o = map.a() + + t.deepEqual(o, []) +}) + +test('map.a-with-items', t => { + const items = [1, 2, 5, 6] + const o = map.a(items) + + t.deepEqual(o, items) +}) + +test('map.a-with-items-and-valuefn', t => { + const items = [1, 2, 5, 6] + const vfn = v => 2*v + const o = map.a(items, {value: vfn}) + + t.deepEqual(o, map.scale(items, 2)) +}) + +test('map.a-with-items-and-valuefn-and-container', t => { + const items = [1, 2, 5, 6] + const vfn = v => 2*v + const container = [20] + const o = map.a(items, {value: vfn, container}) + + t.deepEqual(container, [20, ...map.scale(items, 2)]) + t.deepEqual(o, container) +}) +/////////////////////////// map.a [end] /////////////////////////// + /////////////////////////// reduce.mul [start] ///////////////////////// -test('reduce-mul-no-args', t => { +test('reduce.mul-no-args', t => { const o = reduce.mul() t.is(o, 1) }) -test('reduce-mul-empty-items', t => { +test('reduce.mul-empty-items', t => { const o = reduce.mul([]) t.is(o, 1) }) -test('reduce-mul-list', t => { +test('reduce.mul-list', t => { const items = [1, 2, 5, 6] const o = reduce.mul(items) t.is(o, 60) }) -test('reduce-mul-list-with-some-strings', t => { +test('reduce.mul-list-with-some-strings', t => { const items = [1, 2, "hello", 6] const o = reduce.mul(items) t.truthy(Number.isNaN(o)) }) -test('reduce-mul-list-with-some-boolean', t => { +test('reduce.mul-list-with-some-boolean', t => { const items = [1, 2, 3, true] const o = reduce.mul(items) @@ -65,33 +99,33 @@ test('reduce-mul-list-with-some-boolean', t => { /////////////////////////// reduce.mul [end] ///////////////////////// /////////////////////////// reduce.o [start] ///////////////////////// -test('reduce-o-no-args', t => { +test('reduce.o-no-args', t => { const o = reduce.o() t.deepEqual(o, {}) }) -test('reduce-o-empty-items', t => { +test('reduce.o-empty-items', t => { const o = reduce.o([]) t.deepEqual(o, {}) }) -test('reduce-o-list', t => { +test('reduce.o-list', t => { const items = [1, 2, 5, 6] const o = reduce.o(items) t.deepEqual(o, {1:1, 2:2, 5:5, 6:6}) }) -test('reduce-o-list-with-keyfn-and-valuefn', t => { +test('reduce.o-list-with-keyfn-and-valuefn', t => { const items = [1, 2, 5, 6] const o = reduce.o(items, {key: k => k**2, value: v => v + 2}) t.deepEqual(o, {1:3, 4:4, 25:7, 36:8}) }) -test('reduce-o-list-with-keyfn-and-valuefn-using-indices', t => { +test('reduce.o-list-with-keyfn-and-valuefn-using-indices', t => { const items = [1, 2, 5, 6] const o = reduce.o(items, {key: (k, i) => k*i, value: (v, i) => v+i}) @@ -99,6 +133,36 @@ test('reduce-o-list-with-keyfn-and-valuefn-using-indices', t => { }) /////////////////////////// reduce.o [end] /////////////////////////// +/////////////////////////// reduce.rollingmul [start] /////////////////////////// +test('reduce.rollingmul-no-args', t => { + const o = reduce.rollingmul() + + t.deepEqual(o, []) +}) + +test('reduce.rollingmul-with-items', t => { + const items = [1, 2, 5, 6] + const o = reduce.rollingmul(items) + + t.deepEqual(o, [1, 2, 10, 60]) +}) +/////////////////////////// reduce.rollingmul [end] /////////////////////////// + +/////////////////////////// reduce.sum [start] /////////////////////////// +test('reduce.sum-no-args', t => { + const o = reduce.sum() + + t.is(o, 0) +}) + +test('reduce.sum-with-items', t => { + const items = [1, 2, 3, 4, 6] + const o = reduce.sum(items) + + t.is(o, 16) +}) +/////////////////////////// reduce.sum [end] /////////////////////////// + /////////////////////////// zip [start] /////////////////////////// test('zip-no-args', t => { const zipped = zip() @@ -263,4 +327,104 @@ test('split-each-one-gets-some', t => { t.deepEqual(p, input.slice(start, start + length)) }) }) -/////////////////////////// split [end] /////////////////////////// \ No newline at end of file +/////////////////////////// split [end] /////////////////////////// + +/////////////////////////// ranges [start] /////////////////////////// +test('ranges-no-args', t => { + const o = ranges() + + t.deepEqual(o, []) +}) + +test('ranges-with-single-size', t => { + const o = ranges(4) + + t.deepEqual(o, [[0], [1], [2], [3]]) +}) + +test('ranges-with-multiple-size', t => { + const o = ranges(2, 3) + + t.deepEqual(o, [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]]) +}) +/////////////////////////// ranges [end] /////////////////////////// + +/////////////////////////// last [start] /////////////////////////// +test('last-no-args', t => { + const o = last() + + t.is(o, undefined) +}) + +test('last-with-single-input', t => { + const input = 'last-input' + const o = last(input) + + t.is(o, input) +}) + +test('last-with-single-item', t => { + const items = ['last-input'] + const o = last(items) + + t.is(o, 'last-input') +}) + +test('last-with-multiple-items', t => { + const items = ['last-input0', 'last-input1', 'last-input2'] + const o = last(items) + + t.is(o, 'last-input2') +}) +/////////////////////////// last [end] /////////////////////////// + +/////////////////////////// resolveAddress [start] /////////////////////////// +test('resolveAddress-no-args', t => { + const o = resolveAddress() + + t.deepEqual(o, []) +}) + +test('resolveAddress-with-value', t => { + const value = ['address0'] + const o = resolveAddress(value) + + t.deepEqual(o, value) +}) + +test('resolveAddress-with-1d-address', t => { + const value = ['address0', 'address1'] + const o = resolveAddress(value, [1]) + + t.deepEqual(o, 'address1') +}) + +test('resolveAddress-with-2d-address', t => { + const value = [['address00'], ['address10', 'address11'], 'address20'] + const o = resolveAddress(value, [1, 1]) + + t.deepEqual(o, 'address11') +}) +/////////////////////////// resolveAddress [end] /////////////////////////// + +/////////////////////////// repeat [start] /////////////////////////// +test('repeat-no-args', t => { + const o = repeat() + + t.deepEqual(o, [undefined, undefined]) +}) + +test('repeat-with-object', t => { + const input = {tree: 'tonmayi'} + const o = repeat(input) + + t.deepEqual(o, [input, input]) +}) + +test('repeat-with-object-and-count', t => { + const input = {tree: 'tonmayi'} + const o = repeat(input, 5) + + t.deepEqual(o, fill(6, functions.constant(input))) +}) +/////////////////////////// repeat [end] /////////////////////////// \ No newline at end of file