├── .gitignore
├── .npmignore
├── src
├── copyright-header.txt
└── fpo.src.js
├── .travis.yml
├── tests
├── index.html
└── qunit.config.js
├── scripts
├── node-tests.js
└── build-core.js
├── docs
├── README.md
├── core-API.md
└── std-API.md
├── LICENSE.txt
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .coveralls.yml
2 | node_modules/
3 | dist/
4 | coverage/
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .npmignore
2 | .gitignore
3 | .coveralls.yml
4 | .travis.yml
5 | node_modules/
6 | coverage/
7 |
--------------------------------------------------------------------------------
/src/copyright-header.txt:
--------------------------------------------------------------------------------
1 | /*! fpo.js
2 | v${version} (c) ${year} Kyle Simpson
3 | MIT License: http://getify.mit-license.org
4 | */
5 |
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - 6
5 | - 7
6 | env:
7 | - TEST_PACKAGE=true
8 | branches:
9 | only:
10 | - master
11 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FPO Test Suite
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/scripts/node-tests.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var path = require("path");
4 |
5 | /* istanbul ignore next */
6 | if (process.env.TEST_DIST) {
7 | global.FPO = require(path.join("..","dist","fpo.js"));
8 | }
9 | /* istanbul ignore next */
10 | else if (process.env.TEST_PACKAGE) {
11 | global.FPO = require(path.join(".."));
12 | }
13 | else {
14 | global.FPO = require(path.join("..","src","fpo.src.js"));
15 | }
16 |
17 | global.QUnit = require("qunitjs");
18 |
19 | require("../tests/qunit.config.js");
20 | require("../tests/tests.js");
21 |
22 | QUnit.start();
23 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # FPO.js Docs
2 |
3 | * See [Core API](core-API.md) for documentation on all the methods in the `FPO.*` namespace.
4 | * See [Standard API](std-API.md) for documenation on the methods in the `FPO.std.*` namespace.
5 |
6 | All core methods have a standard positional-parameter form available under the `FPO.std.*` namespace. In many respects, their conceptual behavior is the same, but in some cases there's some differences to be aware of.
7 |
8 | There are also a few methods on the `FPO.std.*` namespace that have no equivalent in the core API as they are unnecessary or don't make sense.
9 |
10 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Kyle Simpson
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fpo",
3 | "version": "6.0.1",
4 | "description": "FP library for JavaScript. Supports named-argument style methods.",
5 | "main": "./dist/fpo.js",
6 | "scripts": {
7 | "test": "node scripts/node-tests.js",
8 | "test:dist": "TEST_DIST=true npm test",
9 | "test:package": "TEST_PACKAGE=true npm test",
10 | "test:all": "npm test && npm run test:dist && npm run test:package",
11 | "coverage": "istanbul cover scripts/node-tests.js",
12 | "coverage:report": "istanbul cover scripts/node-tests.js --report lcovonly && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
13 | "build-core": "node scripts/build-core.js",
14 | "build": "npm run build-core",
15 | "prepare": "npm run build",
16 | "prepublish": "npm run build && npm run test:all",
17 | "publish": "npm run coverage:report"
18 | },
19 | "devDependencies": {
20 | "coveralls": "~2.12.0",
21 | "qunitjs": "~2.1.1"
22 | },
23 | "repository": "getify/fpo",
24 | "keywords": [
25 | "fp",
26 | "functional programming"
27 | ],
28 | "bugs": {
29 | "url": "https://github.com/getify/fpo/issues",
30 | "email": "getify@gmail.com"
31 | },
32 | "homepage": "https://github.com/getify/fpo",
33 | "author": "Kyle Simpson ",
34 | "license": "MIT"
35 | }
36 |
--------------------------------------------------------------------------------
/scripts/build-core.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var fs = require("fs"),
4 | path = require("path"),
5 | // ugly = require("uglify-js"),
6 | packageJSON,
7 | copyrightHeader,
8 | version,
9 | year = (new Date()).getFullYear(),
10 |
11 | ROOT_DIR = path.join(__dirname,".."),
12 | SRC_DIR = path.join(ROOT_DIR,"src"),
13 | DIST_DIR = path.join(ROOT_DIR,"dist"),
14 |
15 | LIB_SRC = path.join(SRC_DIR,"fpo.src.js"),
16 | LIB_DIST = path.join(DIST_DIR,"fpo.js"),
17 |
18 | result
19 | ;
20 |
21 | console.log("*** Building Core ***");
22 | console.log(`Building: ${LIB_DIST}`);
23 |
24 | try {
25 | // try to make the dist directory, if needed
26 | try {
27 | fs.mkdirSync(DIST_DIR,0o755);
28 | }
29 | catch (err) { }
30 |
31 | // NOTE: since uglify doesn't yet support ES6, no minifying happening :(
32 | result = fs.readFileSync(LIB_SRC,{ encoding: "utf8" });
33 |
34 | // result = ugly.minify(path.join(SRC_DIR,"fpo.src.js"),{
35 | // mangle: {
36 | // keep_fnames: true
37 | // },
38 | // compress: {
39 | // keep_fnames: true
40 | // },
41 | // output: {
42 | // comments: /^!/
43 | // }
44 | // });
45 |
46 | // read version number from package.json
47 | packageJSON = JSON.parse(
48 | fs.readFileSync(
49 | path.join(ROOT_DIR,"package.json"),
50 | { encoding: "utf8" }
51 | )
52 | );
53 | version = packageJSON.version;
54 |
55 | // read copyright-header text, render with version and year
56 | copyrightHeader = fs.readFileSync(
57 | path.join(SRC_DIR,"copyright-header.txt"),
58 | { encoding: "utf8" }
59 | ).replace(/`/g,"");
60 | copyrightHeader = Function("version","year",`return \`${copyrightHeader}\`;`)( version, year );
61 |
62 | // append copyright-header text
63 | result = copyrightHeader + result;
64 |
65 | // write dist
66 | fs.writeFileSync( LIB_DIST, result /* result.code + "\n" */, { encoding: "utf8" } );
67 |
68 | console.log("Complete.");
69 | }
70 | catch (err) {
71 | console.error(err);
72 | process.exit(1);
73 | }
74 |
--------------------------------------------------------------------------------
/tests/qunit.config.js:
--------------------------------------------------------------------------------
1 | QUnit.config.requireExpects = true;
2 |
3 | QUnit.begin(begin);
4 | QUnit.log(testLog);
5 | QUnit.testDone(testDone);
6 | QUnit.done(done);
7 |
8 | var testLogEntries = {};
9 |
10 | // ******************************
11 |
12 | function begin(details){
13 | printEnvNotification();
14 |
15 | if (details.totalTests > 0) {
16 | console.log(`FPO Test Suite (${details.totalTests})`);
17 | console.log("");
18 | }
19 | else {
20 | console.log(`FPO Test Suite: empty!`);
21 | process.exit(1);
22 | }
23 | }
24 |
25 | function testLog(details) {
26 | var testId = details.testId;
27 |
28 | testLogEntries[testId] = testLogEntries[testId] || {};
29 | testLogEntries[testId][details.message] = details;
30 | }
31 |
32 | function testDone(results){
33 | var testId = results.testId;
34 |
35 | if (results.failed > 0) {
36 | console.log(`Failed: '${results.name}' (${results.failed}/${results.total})`);
37 | for (let i = 0; i < results.assertions.length; i++) {
38 | if (results.assertions[i].result === false) {
39 | let { message, expected, actual } = testLogEntries[testId][results.assertions[i].message];
40 | console.log(` ${message}`);
41 | console.log(` expected: ${prettyPrint(expected)}`);
42 | console.log(` actual: ${prettyPrint(actual)}`);
43 | }
44 | }
45 | }
46 | else if (results.passed > 0) {
47 | console.log(`Passed: '${results.name}' (${results.passed}/${results.total})`);
48 | }
49 | else {
50 | console.log(`No assertions run: '${results.name}'`);
51 | }
52 | }
53 |
54 | function done(results){
55 | console.log("");
56 |
57 | if (results.failed > 0) {
58 | console.log(`Failed (${results.failed}/${results.total})`);
59 | printEnvNotification();
60 | process.exit(1);
61 | }
62 | else if (results.passed > 0) {
63 | console.log(`Passed (${results.passed}/${results.total})`);
64 | printEnvNotification();
65 | process.exit(0);
66 | }
67 | else {
68 | console.log("No tests run!");
69 | printEnvNotification();
70 | process.exit(1);
71 | }
72 | }
73 |
74 | function prettyPrint(v) {
75 | if (Array.isArray(v)) {
76 | return `[${ v.map( prettyPrint ).toString() }]`;
77 | }
78 | else if (v && typeof v == "object") {
79 | return JSON.stringify(v,function(k,v){
80 | if (v === undefined) {
81 | return null;
82 | }
83 | return v;
84 | });
85 | }
86 | return String(v);
87 | }
88 |
89 | function printEnvNotification() {
90 | console.log("");
91 | console.log("**********************************");
92 | if (process.env.TEST_DIST) {
93 | console.log("********** TESTING DIST **********");
94 | }
95 | else if (process.env.TEST_PACKAGE) {
96 | console.log("******** TESTING PACKAGE *********");
97 | }
98 | else {
99 | console.log("********** TESTING SRC ***********");
100 | }
101 | console.log("**********************************");
102 | console.log("");
103 | }
104 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FPO.js
2 |
3 | [](https://travis-ci.org/getify/fpo)
4 | [](https://www.npmjs.org/package/fpo)
5 | [](https://david-dm.org/getify/fpo)
6 | [](https://david-dm.org/getify/fpo)
7 | [](https://coveralls.io/github/getify/fpo?branch=master)
8 |
9 | FPO (/ˈefpō/) is an FP Library for JavaScript. The main aesthetic difference is that the [`FPO.*` core API methods](docs/core-API.md) are all styled to use named-arguments (object parameter destructuring) instead of individual positional arguments.
10 |
11 | ```js
12 | // positional arguments
13 | foo( 1, 2, 3 );
14 |
15 | // named arguments
16 | foo({ z: 3, x: 1, y: 2 });
17 | ```
18 |
19 | Not only do named-arguments eliminate having to remember a method signature's parameter order -- named arguments can be provided in any order! -- they also make skipping optional parameters (to apply defaults) simple.
20 |
21 | This elimination of ordering concern and/or skipping arguments particularly comes in handy when you're currying. You don't have to juggle the parameter order at all; just pass in whichever named argument(s) you want, in whatever sequence you need!
22 |
23 | The other benefit is that these API methods will automatically work with your program's named-argument style functions. If you need to interoperate between both styles of function parameters in your program, adapt either style to the other using the [`FPO.apply(..)`](docs/core-API.md#fpoapply) and [`FPO.unapply(..)`](docs/core-API.md#fpounapply) methods.
24 |
25 | For convenience and familiarity sake, FPO also exposes all its methods in the traditional positional argument form, under [`FPO.std.*`](docs/std-API.md). These methods are very similar to their equivalents in Ramda, for example.
26 |
27 | **Note:** If you're not fully confident in your FP skills, I've written a book on FP in JavaScript: [Functional-Light JavaScript](https://github.com/getify/functional-light-js). Go check it out!
28 |
29 | ## Environment Support
30 |
31 | This library uses ES6+ features. If you need to support ES<=5 environments, transpile it first (with Babel, etc).
32 |
33 | ## At A Glance
34 |
35 | ```js
36 | // the classic/traditional method style
37 | // (on the `FPO.std.*` namespace)
38 | FPO.std.reduce(
39 | (acc,v) => acc + v,
40 | undefined,
41 | [3,7,9]
42 | ); // 19
43 |
44 | // FPO named-argument method style
45 | FPO.reduce({
46 | arr: [3,7,9],
47 | fn: ({acc,v}) => acc + v
48 | }); // 19
49 | ```
50 |
51 | Instead of needing to provide an `undefined` placeholder in the second argument position to skip specifying an initial value, named-argument style allows us to just omit that argument. We also specified `arr` first and `fn` second just to show order doesn't matter anymore!
52 |
53 | As with most FP libraries, all public FPO methods are curried. Currying with named-arguments (in any sequence!) is a breeze:
54 |
55 | ```js
56 | var f = FPO.reduce({ arr: [3,7,9] });
57 |
58 | // later:
59 | f( {fn: ({acc,v}) => acc + v} ); // 19
60 | f( {fn: ({acc,v}) => acc * v} ); // 189
61 | ```
62 |
63 | The equivalent using a standard FP library would look like:
64 |
65 | ```js
66 | var f = curry( flip( partialRight( reduce, [[3,7,9]] ) ) )( 0 );
67 |
68 | f( (acc,v) => acc + v ); // 19
69 | ```
70 |
71 | Phew, that's a lot of argument juggling! FPO eliminates all that noisy distraction.
72 |
73 | ## API
74 |
75 | * See [Core API](docs/core-API.md) for documentation on all the methods in the `FPO.*` namespace.
76 | * See [Standard API](docs/std-API.md) for documenation on the methods in the `FPO.std.*` namespace.
77 |
78 | All core methods have a standard positional-parameter form available under the `FPO.std.*` namespace. In many respects, their conceptual behavior is the same, but in some cases there's some differences to be aware of.
79 |
80 | There are also a few methods on the `FPO.std.*` namespace that have no equivalent in the core API as they are unnecessary or don't make sense.
81 |
82 | ## Adapting
83 |
84 | What if you have a traditional parameter-style function you want to use with one of the object-parameter style FPO API methods? Adapt it using the object-parameter-aware [`FPO.apply(..)`](docs/core-API.md#fpoapply):
85 |
86 | ```js
87 | // traditional-parameter style function
88 | function add(acc,v) { return acc + v; }
89 |
90 | FPO.reduce({
91 | arr: [3,7,9],
92 | fn: FPO.apply( {fn: add} )
93 | }); // 19
94 | ```
95 |
96 | Adapting isn't limited to just interoperating with FPO methods. You can use [`FPO.apply(..)`](docs/core-API.md#fpoapply) and [`FPO.unapply(..)`](docs/core-API.md#fpounapply) to seamlessly adapt between functions of both styles in your own application code:
97 |
98 | ```js
99 | // note: cb is expected to be an "error-first" style
100 | // traditional callback function
101 | function ajax(url,cb) { .. }
102 |
103 | // our object-parameter style function
104 | function onResponse({ resp, err }) {
105 | if (!err) {
106 | console.log( resp );
107 | }
108 | }
109 |
110 | // adapt `ajax(..)` to accept named arguments
111 | var request = FPO.apply( {fn: ajax} );
112 |
113 | // now we can provide arguments in any order!
114 | request({
115 | cb:
116 | // adapt the object-parameter style `onResponse(..)`
117 | // to work as a standard positional-argument function, as
118 | // expected by `ajax(..)`
119 | FPO.unapply( {fn: onResponse, props:["err","resp"]} ),
120 |
121 | url: "http://some.url"
122 | });
123 | ```
124 |
125 | ### Remapping Function Parameters/Arguments
126 |
127 | What if you have a function that expects a certain named parameter, but it needs to accept a differently named argument? For example:
128 |
129 | ```js
130 | function uppercase({ str }) { return str.toUpperCase(); }
131 |
132 | FPO.map( {fn: uppercase, arr: ["hello","world"]} );
133 | // TypeError (`str` is undefined)
134 | ```
135 |
136 | The problem here is that [`FPO.map(..)`](docs/core-API.md#fpomap) expects to call its mapper function with a `v` named argument, but `uppercase(..)` expects `str`.
137 |
138 | [`FPO.remap(..)`](docs/core-API.md#fporemap) -- despite the name similarity, no particular relationship to [`FPO.map(..)`](docs/core-API.md#fpomap), except that it's our example -- lets you adapt a function to remap its expected named parameters:
139 |
140 | ```js
141 | function uppercase({ str }) { return str.toUpperCase(); }
142 |
143 | FPO.map( {
144 | fn: FPO.remap( {fn: uppercase, args: {str: "v"}} ),
145 | arr: ["hello","world"]
146 | } );
147 | // ["HELLO","WORLD"]
148 | ```
149 |
150 | **Note:** The [`FPO.remap(..)`](docs/core-API.md#fporemap) utility passes through any non-remapped arguments as-is.
151 |
152 | ## Not Order, But Names
153 |
154 | The exchange we make for not needing to remember or juggle argument order is that we need to know/remember the parameter names. For example, [`FPO.reduce(..)`](docs/core-API.md#fporeduce) expects named arguments of `fn`, `v`, and `arr`. If you don't use those names, it won't work correctly.
155 |
156 | To aid in getting used to that tradeoff in usability, FPO uses straightforward conventions for parameter names; once learned, it should be mostly trivial to use any of the API methods.
157 |
158 | The named argument naming conventions (in order of precedence):
159 |
160 | * When a method expects a function, the named argument is `fn`.
161 | * When a method expects a number, the named argument is `n`.
162 | * When a method expects a value, the named argument is `v`.
163 | * When a method expects an array of functions, the named argument is `fns`.
164 | * When a method expects a single array, the named argument is `arr`.
165 | * When a method expects two arrays, the named arguments are `arr1` and `arr2`.
166 | * When a method expects a single object-property name, the named argument is `prop`.
167 | * When a method expects a list of object-property names, the named argument is `props`.
168 | * When a mapper function is called, it will be passed these named arguments: `v` (value), `i` (index), `arr` (array).
169 | * When a predicate function is called, it will be passed these named arguments: `v` (value), `i` (index), `arr` (array).
170 | * When a reducer function is called, it will be passed these named arguments: `acc` (accumulator), `v` (value), `i` (index), `arr` (array).
171 | * When a transducer combination function is called, it will be passed these named arguments: `acc` (accumulator), `v` (value).
172 |
173 | Some exceptions to these naming conventions:
174 |
175 | * [`FPO.setProp(..)`](docs/core-API.md#fposetprop) expects: `prop` (object-property name), `o` (object), `v` (value).
176 | * [`FPO.partial(..)`](docs/core-API.md#fpopartial) expects: `fn` (function), `args` (object containing the named-argument/value pairs to partially apply).
177 | * [`FPO.flatten(..)`](docs/core-API.md#fpoflatten) expects: `v` (array), `n` (count of nesting levels to flatten out).
178 | * [`FPO.transduce(..)`](docs/core-API.md#fpotransduce) expects: `fn` (transducer function), `co` (combination function), `v` (initial value), `arr` (array).
179 | * [`FPO.compose(..)`](docs/core-API.md#fpocompose) and [`FPO.pipe(..)`](core.API.md#fpopipe) produce functions that expect a `{ v: .. }` object argument. These utilities further assume that each function in the composition expects the output of the previous function to be rewrapped in a `{ v: .. }`-style object argument.
180 |
181 | This also applies to transducers. [`FPO.transducers.filter(..)`](docs/core-API.md#fpotransducersfilter) and [`FPO.transducers.map(..)`](docs/core-API.md#fpotransducersmap), whether composed together or used standalone, are curried to expect the combination function to be passed to them as a `{ v: .. }`-style object argument.
182 | * [`FPO.reassoc(..)`](docs/core-API.md#fporeassoc) expects: `props` (object with `sourceProp: targetProp` remapping key/value pairs), `v` (object)
183 |
184 | ## Arity
185 |
186 | Arity is typically defined as the number of declared parameters (expected arguments) for a function. With traditional style functions, this is just a simple numeric count.
187 |
188 | For named-argument style functions, the situation is more nuanced. One interpretation of arity would be a raw count of named-arguments (how many properties present). Another interpretation would limit this counting to only the set of expected named-arguments.
189 |
190 | For currying, FPO assumes the straight numeric count interpretation. [`FPO.curry( {fn: foo, n: 3} )`](docs/core-API.md#fpocurry) makes a curried function that accepts the first 3 properties, one at a time, regardless of what they're named.
191 |
192 | For [`unary(..)`](docs/core-API.md#fpounary), [`binary(..)`](docs/core-API.md#fpobinary), and [`nAry(..)`](docs/core-API.md#fponary), FPO requires a list of properties (`props`) to filter through for the underlying function. `FPO.binary({fn: foo, props:["x","y"]})` makes a function that only lets `x` and `y` named arguments through to `foo(..)`.
193 |
194 | ## Currying
195 |
196 | The strictest definition of currying is that each call only allows through one argument (`foo(1)(2)(3)`). That's consistent with how currying works in Haskell, for example.
197 |
198 | However, for convenience, most FP libraries in JS use a looser definition of currying where multiple arguments can be passed in with each call (`foo(1,2)(3)`).
199 |
200 | FPO supports both approaches. [`FPO.curry(..)`](docs/core-API.md#fpocurry) (and [`FPO.std.curry(..)`](docs/std-API.md#fpostdcurry)) use the stricter one-at-a-time definition -- subsequent arguments in each call are ignored -- while [`FPO.curryMultiple(..)`](docs/core-API.md#fpocurrymultiple) (and [`FPO.std.curryMultiple(..)`](docs/std-API.md#fpostdcurrymultiple)) use the looser multiple-arguments definition.
201 |
202 | All FPO methods are multiple-curried for convenience.
203 |
204 | ## Builds
205 |
206 | [](https://travis-ci.org/getify/fpo)
207 | [](https://www.npmjs.org/package/fpo)
208 |
209 | The distribution library file (`dist/fpo.js`) comes pre-built with the npm package distribution, so you shouldn't need to rebuild it under normal circumstances.
210 |
211 | However, if you download this repository via Git:
212 |
213 | 1. The included build utility (`scripts/build-core.js`) builds (and ~~minifies~~) `dist/fpo.js` from source. **Note:** Minification is currently disabled. **The build utility expects Node.js version 6+.**
214 |
215 | 2. To install the build and test dependencies, run `npm install` from the project root directory.
216 |
217 | 3. Because of how npm lifecyle events (currently: npm v4) work, `npm install` will have the side effect of automatically running the build and test utilities for you. So, no further action should be needed on your part. Starting with npm v5, the build utility will still be run automatically on `npm install`, but the test utility will not.
218 |
219 | To run the build utility with npm:
220 |
221 | ```
222 | npm run build
223 | ```
224 |
225 | To run the build utility directly without npm:
226 |
227 | ```
228 | node scripts/build-core.js
229 | ```
230 |
231 | ## Tests
232 |
233 | A comprehensive test suite is included in this repository, as well as the npm package distribution. The default test behavior runs the test suite using `src/fpo.src.js`.
234 |
235 | 1. You can run the tests in a browser by opening up `tests/index.html` (**requires ES6+ browser environment**).
236 |
237 | 2. The included Node.js test utility (`scripts/node-tests.js`) runs the test suite. **This test utility expects Node.js version 6+.**
238 |
239 | 3. Ensure the Node.js test utility dependencies are installed by running `npm install` from the project root directory.
240 |
241 | 4. Because of how npm lifecyle events (currently: npm v4) work, `npm install` will have the side effect of automatically running the build and test utilities for you. So, no further action should be needed on your part. Starting with npm v5, the build utility will still be run automatically on `npm install`, but the test utility will not.
242 |
243 | To run the test utility with npm:
244 |
245 | ```
246 | npm test
247 | ```
248 |
249 | Other npm test scripts:
250 |
251 | * `npm run test:dist` will run the test suite against `dist/fpo.js`.
252 |
253 | * `npm run test:package` will run the test suite as if the package had just been installed via npm. This ensures `package.json`:`main` properly references `dist/fpo.js` for inclusion.
254 |
255 | * `npm run test:all` will run all three modes of the test suite. This is what's automatically run when you first `npm install` the build and test dependencies.
256 |
257 | To run the test utility directly without npm:
258 |
259 | ```
260 | node scripts/node-tests.js
261 | ```
262 |
263 | ### Test Coverage
264 |
265 | [](https://coveralls.io/github/getify/fpo?branch=master)
266 |
267 | If you have [Istanbul](https://github.com/gotwarlost/istanbul) already installed on your system (requires v1.0+), you can use it to check the test coverage:
268 |
269 | ```
270 | npm run coverage
271 | ```
272 |
273 | Then open up `coverage/lcov-report/index.html` in a browser to view the report.
274 |
275 | To run Istanbul directly without npm:
276 |
277 | ```
278 | istanbul cover scripts/node-tests.js
279 | ```
280 |
281 | **Note:** The npm script `coverage:report` is only intended for use by project maintainers. It sends coverage reports to [Coveralls](https://coveralls.io/).
282 |
283 | ## License
284 |
285 | All code and documentation are (c) 2017 Kyle Simpson and released under the [MIT License](http://getify.mit-license.org/). A copy of the MIT License [is also included](LICENSE.txt).
286 |
--------------------------------------------------------------------------------
/src/fpo.src.js:
--------------------------------------------------------------------------------
1 | (function UMD(name,context,definition){
2 | /* istanbul ignore next */if (typeof define === "function" && define.amd) { define(definition); }
3 | /* istanbul ignore next */else if (typeof module !== "undefined" && module.exports) { module.exports = definition(); }
4 | /* istanbul ignore next */else { context[name] = definition(name,context); }
5 | })("FPO",this,function DEF(name,context){
6 | "use strict";
7 |
8 | var publicAPI = {
9 | identity: curryMultiple( {fn: identity, n: 1} ),
10 | constant: curryMultiple( {fn: constant, n: 1} ),
11 | pick: curryMultiple( {fn: pick, n: 2} ),
12 | pickAll: curryMultiple( {fn: pickAll, n: 2} ),
13 | nAry: curryMultiple( {fn: nAry, n: 2} ),
14 | unary: curryMultiple( {fn: unary, n: 2} ),
15 | binary: curryMultiple( {fn: binary, n: 2} ),
16 | curry: curryMultiple( {fn: curry, n: 1} ),
17 | curryMultiple: curryMultiple( {fn: curryMultiple, n: 1} ),
18 | uncurry: curryMultiple( {fn: uncurry, n: 1} ),
19 | partial: curryMultiple( {fn: partial, n: 2 } ),
20 | complement: curryMultiple( {fn: complement, n: 1} ),
21 | apply: curryMultiple( {fn: apply, n: 1} ),
22 | unapply: curryMultiple( {fn: unapply, n: 2} ),
23 | compose: curryMultiple( {fn: compose, n: 1} ),
24 | pipe: curryMultiple( {fn: pipe, n: 1} ),
25 | prop: curryMultiple( {fn: prop, n: 2} ),
26 | setProp: curryMultiple( {fn: setProp, n: 3} ),
27 | reassoc: curryMultiple( {fn: reassoc, n: 2} ),
28 | filterIn: curryMultiple( {fn: filterIn, n: 2} ),
29 | filterInObj: curryMultiple( {fn: filterInObj, n: 2} ),
30 | filterOut: curryMultiple( {fn: filterOut, n: 2} ),
31 | filterOutObj: curryMultiple( {fn: filterOutObj, n: 2} ),
32 | map: curryMultiple( {fn: map, n: 2} ),
33 | mapObj: curryMultiple( {fn: mapObj, n: 2} ),
34 | flatMap: curryMultiple( {fn: flatMap, n: 2} ),
35 | flatMapObj: curryMultiple( {fn: flatMapObj, n: 2} ),
36 | reduce: curryMultiple( {fn: reduce, n: 2} ),
37 | reduceObj: curryMultiple( {fn: reduceObj, n: 2} ),
38 | reduceRight: curryMultiple( {fn: reduceRight, n: 2} ),
39 | flatten: curryMultiple( {fn: flatten, n:1} ),
40 | zip: curryMultiple( {fn: zip, n: 2} ),
41 | trampoline: curryMultiple( {fn: trampoline, n: 1} ),
42 | transducers: {
43 | transduce: curryMultiple( {fn: transduce, n: 4} ),
44 | into: curryMultiple( {fn: into, n: 3} ),
45 | map: curryMultiple( {fn: transducerMap, n: 1} ),
46 | filter: curryMultiple( {fn: transducerFilter, n: 1} ),
47 | string: curryMultiple( {fn: strConcat, n: 2} ),
48 | array: curryMultiple( {fn: arrayPush, n: 2} ),
49 | number: curryMultiple( {fn: numericAdd, n: 2} ),
50 | booleanAnd: curryMultiple( {fn: booleanAnd, n: 2} ),
51 | booleanOr: curryMultiple( {fn: booleanOr, n: 2} ),
52 | default: curryMultiple( {fn: ({ acc }) => acc, n: 1} ),
53 | },
54 | head: curryMultiple( {fn: head, n: 1} ),
55 | tail: curryMultiple( {fn: tail, n: 1} ),
56 | take: curryMultiple( {fn: take, n: 1} ),
57 | memoize: curryMultiple( {fn: memoize, n: 1} ),
58 | remap: curryMultiple( {fn: remap, n: 2} ),
59 | };
60 |
61 | publicAPI.std = {
62 | identity: stdCurryMultiple( unapply( {fn: identity, props: ["v"]} ), /*arity=*/1 ),
63 | constant: stdCurryMultiple( unapply( {fn: constant, props: ["v"]} ), /*arity=*/1 ),
64 | pick: stdCurryMultiple( unapply( {fn: pick, props: ["props","v"]} ), /*arity=*/2 ),
65 | pickAll: stdCurryMultiple( unapply( {fn: pickAll, props: ["props","v"]} ), /*arity=*/2 ),
66 | nAry: stdCurryMultiple( stdNAry, /*arity=*/2 ),
67 | unary: stdCurryMultiple( stdUnary, /*arity=*/1 ),
68 | binary: stdCurryMultiple( stdBinary, /*arity=*/1 ),
69 | curry: stdCurryMultiple( stdCurry, /*arity=*/1 ),
70 | curryMultiple: stdCurryMultiple( stdCurryMultiple, /*arity=*/1 ),
71 | uncurry: stdCurryMultiple( stdUncurry, /*arity=*/1 ),
72 | partial: stdCurryMultiple( stdPartial, /*arity=*/2 ),
73 | partialRight: stdCurryMultiple( stdPartialRight, /*arity=*/2 ),
74 | complement: stdCurryMultiple( unapply( {fn: complement, props: ["fn"]} ), /*arity=*/1 ),
75 | apply: stdCurryMultiple( stdApply, /*arity=*/1 ),
76 | unapply: stdCurryMultiple( stdUnapply, /*arity=*/1 ),
77 | compose: stdCurryMultiple( stdCompose, /*arity=*/1 ),
78 | pipe: stdCurryMultiple( stdPipe, /*arity=*/1 ),
79 | prop: stdCurryMultiple( unapply( {fn: prop, props: ["prop","v"]} ), /*arity=*/2 ),
80 | setProp: stdCurryMultiple( unapply( {fn: setProp, props: ["prop","o","v"]} ), /*arity=*/3 ),
81 | reassoc: stdCurryMultiple( unapply( {fn: reassoc, props: ["props","v"]} ), /*arity=*/2 ),
82 | filterIn: stdCurryMultiple( unapply( {fn: _applyFnProp( filterIn, ["v","i","arr"] ), props: ["fn","arr"]} ), /*arity=*/2 ),
83 | filterInObj: stdCurryMultiple( unapply( {fn: _applyFnProp( filterInObj, ["v","i","o"] ), props: ["fn","o"]} ), /*arity=*/2 ),
84 | filterOut: stdCurryMultiple( unapply( {fn: _applyFnProp( filterOut, ["v","i","arr"] ), props: ["fn","arr"]} ), /*arity=*/2 ),
85 | filterOutObj: stdCurryMultiple( unapply( {fn: _applyFnProp( filterOutObj, ["v","i","o"] ), props: ["fn","o"]} ), /*arity=*/2 ),
86 | map: stdCurryMultiple( unapply( {fn: _applyFnProp( map, ["v","i","arr"] ), props: ["fn","arr"]} ), /*arity=*/2 ),
87 | mapObj: stdCurryMultiple( unapply( {fn: _applyFnProp( mapObj, ["v","i","o"] ), props: ["fn","o"]} ), /*arity=*/2 ),
88 | flatMap: stdCurryMultiple( unapply( {fn: _applyFnProp( flatMap, ["v","i","arr"] ), props: ["fn","arr"]}), /*arity=*/2 ),
89 | flatMapObj: stdCurryMultiple( unapply( {fn: _applyFnProp( flatMapObj, ["v","i","o"] ), props: ["fn","o"]}), /*arity=*/2 ),
90 | reduce: stdCurryMultiple( unapply( {fn: _applyFnProp( reduce, ["acc","v","i","arr"] ), props: ["fn","v","arr"]} ), /*arity=*/3 ),
91 | reduceObj: stdCurryMultiple( unapply( {fn: _applyFnProp( reduceObj, ["acc","v","i","o"] ), props: ["fn","v","o"]} ), /*arity=*/3 ),
92 | reduceRight: stdCurryMultiple( unapply( {fn: _applyFnProp( reduceRight, ["acc","v","i","arr"] ), props: ["fn","v","arr"]} ), /*arity=*/3 ),
93 | flatten: stdCurryMultiple( unapply( {fn: flatten, props: ["v","n"]} ), /*arity=*/1 ),
94 | zip: stdCurryMultiple( unapply( {fn: zip, props: ["arr1","arr2"]} ), /*arity=*/2 ),
95 | trampoline: stdCurryMultiple( unapply( {fn: trampoline, props: ["fn"]} ), /*arity=*/1 ),
96 | transducers: {
97 | transduce: stdCurryMultiple( stdTransduce, /*arity=*/4 ),
98 | into: stdCurryMultiple( stdInto, /*arity=*/3 ),
99 | map: stdCurryMultiple( stdTransducerMap, /*arity=*/2 ),
100 | filter: stdCurryMultiple( stdTransducerFilter, /*arity=*/2 ),
101 | string: stdCurryMultiple( stdStrConcat, /*arity=*/2 ),
102 | array: stdCurryMultiple( stdArrayPush, /*arity=*/2 ),
103 | number: stdCurryMultiple( stdNumericAdd, /*arity=*/2 ),
104 | booleanAnd: stdCurryMultiple( stdBooleanAnd, /*arity=*/2 ),
105 | booleanOr: stdCurryMultiple( stdBooleanOr, /*arity=*/2 ),
106 | default: stdCurryMultiple( acc => acc, /*arity=*/1 ),
107 | },
108 | flip: stdCurryMultiple( stdFlip, /*arity=*/1 ),
109 | reverseArgs: stdCurryMultiple( stdReverseArgs, /*arity=*/1 ),
110 | head: stdCurryMultiple( unapply( {fn: head, props: ["v"]} ), /*arity=*/1 ),
111 | tail: stdCurryMultiple( unapply( {fn: tail, props: ["v"]} ), /*arity=*/1 ),
112 | take: stdCurryMultiple( unapply( {fn: take, props: ["v","n"]} ), /*arity=*/1 ),
113 | memoize: stdCurryMultiple( unapply( {fn: memoize, props: ["fn","n"]} ), /*arity=*/1 ),
114 | remap: stdCurryMultiple( unapply( {fn: remap, props: ["fn","args"]} ), /*arity=*/2 ),
115 | };
116 |
117 | // method convenience aliases
118 | _setMethodAlias("constant","always");
119 | _setMethodAlias("pipe","flow");
120 | _setMethodAlias("pipe","sequence");
121 | _setMethodAlias("compose","flowRight");
122 | _setMethodAlias("apply","spread");
123 | _setMethodAlias("unapply","gather");
124 | _setMethodAlias("setProp","assoc");
125 | _setMethodAlias("filterIn","filter");
126 | _setMethodAlias("filterIn","keep");
127 | _setMethodAlias("filterInObj","filterObj");
128 | _setMethodAlias("filterInObj","keepObj");
129 | _setMethodAlias("filterOut","reject");
130 | _setMethodAlias("filterOutObj","rejectObj");
131 | _setMethodAlias("flatMap","chain");
132 | _setMethodAlias("flatMapObj","chainObj");
133 | _setMethodAlias("reduce","fold");
134 | _setMethodAlias("reduceObj","foldObj");
135 | _setMethodAlias("reduce","foldL");
136 | _setMethodAlias("reduceRight","foldR");
137 | publicAPI.partialRight = publicAPI.partial;
138 | publicAPI.transducers.boolean = publicAPI.transducers.booleanAnd;
139 | publicAPI.std.transducers.boolean = publicAPI.std.transducers.booleanAnd;
140 |
141 | return publicAPI;
142 |
143 |
144 | // ***************************************
145 |
146 | function identity({ v }) {
147 | return v;
148 | }
149 |
150 | function constant({ v }) {
151 | return function value(){
152 | return v;
153 | };
154 | }
155 |
156 | function pick({ v: obj, props = [] }) {
157 | var newObj = {};
158 |
159 | for (let prop of props) {
160 | if (_hasProp( obj, prop )) {
161 | newObj[prop] = obj[prop];
162 | }
163 | }
164 |
165 | return newObj;
166 | }
167 |
168 | function pickAll({ v: obj, props = [] }) {
169 | var newObj = {};
170 |
171 | for (let prop of props) {
172 | newObj[prop] = obj[prop];
173 | }
174 |
175 | return newObj;
176 | }
177 |
178 | function nAry({ fn, props = [] }) {
179 | return function limited(argsObj = {}){
180 | return fn( pick( {v: argsObj, props} ) );
181 | };
182 | }
183 |
184 | function stdNAry(fn,n = 0) {
185 | n = Number( n );
186 |
187 | return function nary(...args){
188 | return fn( ...args.slice( 0, Math.max( 0, n ) ) );
189 | };
190 | }
191 |
192 | function unary({ fn, prop: propName1 = "" }) {
193 | return nAry( {fn, props: [propName1]} );
194 | }
195 |
196 | function stdUnary(fn) {
197 | return stdNAry( fn, 1 );
198 | }
199 |
200 | function binary({ fn, props: [ propName1 = "", propName2 = "" ] = [] }) {
201 | return nAry( {fn, props: [propName1,propName2]} );
202 | }
203 |
204 | function stdBinary(fn) {
205 | return stdNAry( fn, 2 );
206 | }
207 |
208 | function curry({ fn, n: arity = 1 }) {
209 | arity = Number( arity );
210 |
211 | return (function nextCurried(prevArgsObj){
212 | return function curried(nextArgsObj = {}){
213 | var keys = Object.keys( nextArgsObj );
214 | var allArgsObj = (keys.length > 0) ?
215 | Object.assign( {}, prevArgsObj, {[keys[0]]: nextArgsObj[keys[0]]} ) :
216 | prevArgsObj;
217 |
218 | if (Object.keys( allArgsObj ).length >= arity) {
219 | return fn( allArgsObj );
220 | }
221 | else {
222 | return nextCurried( allArgsObj );
223 | }
224 | };
225 | })( {} );
226 | }
227 |
228 | function stdCurry(fn,arity = Math.max( 1, fn.length)) {
229 | arity = Number( arity );
230 |
231 | return (function nextCurried(prevArgs){
232 | return function curried(...nextArgs){
233 | var allArgs = (nextArgs.length > 0) ?
234 | prevArgs.concat( [nextArgs[0]] ) :
235 | prevArgs;
236 |
237 | if (allArgs.length >= arity) {
238 | return fn( ...allArgs );
239 | }
240 | else {
241 | return nextCurried( allArgs );
242 | }
243 | };
244 | })( [] );
245 | }
246 |
247 | function curryMultiple({ fn, n: arity = 1 }) {
248 | arity = Number( arity );
249 |
250 | return (function nextCurried(prevArgsObj){
251 | return function curried(nextArgsObj = {}){
252 | var allArgsObj = (Object.keys( nextArgsObj ).length > 0) ?
253 | Object.assign( {}, prevArgsObj, nextArgsObj ) :
254 | prevArgsObj;
255 |
256 | if (Object.keys( allArgsObj ).length >= arity) {
257 | return fn( allArgsObj );
258 | }
259 | else {
260 | return nextCurried( allArgsObj );
261 | }
262 | };
263 | })( {} );
264 | }
265 |
266 | function stdCurryMultiple(fn,arity = Math.max( 1, fn.length)) {
267 | arity = Number( arity );
268 |
269 | return (function nextCurried(prevArgs){
270 | return function curried(...nextArgs){
271 | var allArgs = (nextArgs.length > 0) ?
272 | prevArgs.concat( nextArgs ) :
273 | prevArgs;
274 |
275 | if (allArgs.length >= arity) {
276 | return fn( ...allArgs );
277 | }
278 | else {
279 | return nextCurried( allArgs );
280 | }
281 | };
282 | })( [] );
283 | }
284 |
285 | function uncurry({ fn }) {
286 | return function uncurried(argsObj = {}){
287 | var ret = fn;
288 |
289 | for (let prop of Object.keys( argsObj )) {
290 | // assume `fn` is strictly curried (needs props one at a time),
291 | // instead of loose/multiple currying
292 | ret = ret( {[prop]: argsObj[prop]} );
293 | }
294 |
295 | return ret;
296 | };
297 | }
298 |
299 | function stdUncurry(fn) {
300 | return function uncurried(...args){
301 | var ret = fn;
302 |
303 | for (let v of args) {
304 | // assume `fn` is strictly curried (needs one arg at a time),
305 | // instead of loose/multiple currying
306 | ret = ret( v );
307 | }
308 |
309 | return ret;
310 | };
311 | }
312 |
313 | function partial({ fn, args: partialArgsObj = {} }) {
314 | return function partiallyApplied(restArgsObj = {}){
315 | return fn( Object.assign( {}, partialArgsObj, restArgsObj ) );
316 | };
317 | }
318 |
319 | function stdPartial(fn,partialArgs = []) {
320 | return function partiallyApplied(...restArgs){
321 | return fn( ...partialArgs, ...restArgs );
322 | };
323 | }
324 |
325 | function stdPartialRight(fn,partialArgs = []) {
326 | return function partiallyApplied(...restArgs){
327 | return fn( ...restArgs, ...partialArgs );
328 | };
329 | }
330 |
331 | function complement({ fn: predicateFn }) {
332 | return function complemented(...args){
333 | return !predicateFn( ...args );
334 | };
335 | }
336 |
337 | function apply({
338 | fn,
339 | props: propNamesInOrder = fn.toString()
340 | .replace( /^(?:(?:function.*\(([^]*?)\))|(?:([^\(\)]+?)\s*=>)|(?:\(([^]*?)\)\s*=>))[^]+$/, "$1$2$3" )
341 | .split( /\s*,\s*/ )
342 | .map( v => v.replace( /[=\s].*$/, "" ) )
343 | }) {
344 | return function appliedFn(argsObj) {
345 | return fn( ...propNamesInOrder.map( function getPropVal(k) { return argsObj[k]; } ) );
346 | };
347 | }
348 |
349 | function stdApply(fn) {
350 | return function appliedFn(argsArr) {
351 | return fn( ...argsArr );
352 | };
353 | }
354 |
355 | function unapply({ fn, props: propNamesInOrder = [] }) {
356 | return function unappliedFn(...args) {
357 | var argsObj = {};
358 | var i1 = 0;
359 | var i2 = 0;
360 |
361 | while (i1 < propNamesInOrder.length && i2 < args.length) {
362 | argsObj[propNamesInOrder[i1++]] = args[i2++];
363 | }
364 |
365 | return fn( argsObj );
366 | };
367 | }
368 |
369 | function stdUnapply(fn) {
370 | return function unappliedFn(...argsArr) {
371 | return fn( argsArr );
372 | };
373 | }
374 |
375 | function compose({ fns = [] }) {
376 | return function composed( {v: result} ){
377 | for (let i = fns.length - 1; i >= 0; i--) {
378 | result = fns[i]( {v: result} );
379 | }
380 |
381 | return result;
382 | };
383 | }
384 |
385 | function stdCompose(fns = []) {
386 | return function composed(result){
387 | for (let i = fns.length - 1; i >= 0; i--) {
388 | result = fns[i]( result );
389 | }
390 |
391 | return result;
392 | };
393 | }
394 |
395 | function pipe({ fns = [] }) {
396 | return function piped( {v: result} ){
397 | for (let fn of fns) {
398 | result = fn( {v: result} );
399 | }
400 |
401 | return result;
402 | };
403 | }
404 |
405 | function stdPipe(fns = []) {
406 | return function piped(result){
407 | for (let fn of fns) {
408 | result = fn( result );
409 | }
410 |
411 | return result;
412 | };
413 | }
414 |
415 | function prop({ prop = "", v: obj = {} }) {
416 | return obj[prop];
417 | }
418 |
419 | function setProp({ prop = "", o: obj = {}, v }) {
420 | obj = Object.assign( {}, obj );
421 | obj[prop] = v;
422 | return obj;
423 | }
424 |
425 | function reassoc({ props = {}, v }) {
426 | var obj = {};
427 | var sourceProps = Object.keys( props );
428 |
429 | // first, remap specified properties
430 | for (let sourceProp of sourceProps) {
431 | if (sourceProp in v) {
432 | obj[props[sourceProp]] = v[sourceProp];
433 | }
434 | }
435 |
436 | // then, copy (only) other properties
437 | for (let prop of Object.keys( v )) {
438 | if (!~sourceProps.indexOf( prop )) {
439 | obj[prop] = v[prop];
440 | }
441 | }
442 |
443 | return obj;
444 | }
445 |
446 | function filterIn({ fn: predicateFn, arr = [] }) {
447 | var newArr = [];
448 |
449 | for (let [i,v] of arr.entries()) {
450 | if (predicateFn( {v, i, arr} )) {
451 | newArr[newArr.length] = v;
452 | }
453 | }
454 |
455 | return newArr;
456 | }
457 |
458 | function filterInObj({ fn: predicateFn, o = {} }) {
459 | var newObj = {};
460 |
461 | for (let i of Object.keys( o )) {
462 | if (predicateFn( {v: o[i], i, o} )) {
463 | newObj[i] = o[i];
464 | }
465 | }
466 |
467 | return newObj;
468 | }
469 |
470 | function filterOut({ fn: predicateFn, arr = [] }) {
471 | return filterIn( {fn: complement( {fn: predicateFn} ), arr} );
472 | }
473 |
474 | function filterOutObj({ fn: predicateFn, o = {} }) {
475 | return filterInObj( {fn: complement( {fn: predicateFn} ), o} );
476 | }
477 |
478 | function map({ fn: mapperFn, arr = [] }) {
479 | var newArr = [];
480 |
481 | for (let [i,v] of arr.entries()) {
482 | newArr[i] = mapperFn( {v, i, arr} );
483 | }
484 |
485 | return newArr;
486 | }
487 |
488 | function mapObj({ fn: mapperFn, o = {} }) {
489 | var newObj = {};
490 |
491 | for (let i of Object.keys( o )) {
492 | newObj[i] = mapperFn( {v: o[i], i, o} );
493 | }
494 |
495 | return newObj;
496 | }
497 |
498 | function flatMap({ fn: mapperFn, arr = [] }) {
499 | var newArr = [];
500 |
501 | for (let [i,v] of arr.entries()) {
502 | newArr = newArr.concat( mapperFn( {v, i, arr} ) );
503 | }
504 |
505 | return newArr;
506 | }
507 |
508 | function flatMapObj({ fn: mapperFn, o = {} }) {
509 | var newObj = {};
510 |
511 | for (let i of Object.keys(o)) {
512 | let ret = mapperFn( {v: o[i], i, o} );
513 | if (typeof ret == "object" && !Array.isArray( ret )) {
514 | Object.assign( newObj, ret );
515 | }
516 | else {
517 | newObj[i] = ret;
518 | }
519 | }
520 |
521 | return newObj;
522 | }
523 |
524 | function reduce({ fn: reducerFn, v: initialValue, arr = [] }) {
525 | var origArr = arr;
526 | var i = 0;
527 |
528 | if (initialValue === undefined && arr.length > 0) {
529 | initialValue = arr[0];
530 | arr = arr.slice( 1 );
531 | i++;
532 | }
533 |
534 | for (let v of arr) {
535 | initialValue = reducerFn( {acc: initialValue, v, i: i++, arr: origArr} );
536 | }
537 |
538 | return initialValue;
539 | }
540 |
541 | function reduceObj({ fn: reducerFn, v: initialValue, o = {} }) {
542 | var keys = Object.keys( o );
543 |
544 | if (initialValue === undefined && keys.length > 0) {
545 | initialValue = o[keys[0]];
546 | keys = keys.slice( 1 );
547 | }
548 |
549 | for (let i of keys) {
550 | initialValue = reducerFn( {acc: initialValue, v: o[i], i, o} );
551 | }
552 |
553 | return initialValue;
554 | }
555 |
556 | function reduceRight({ fn: reducerFn, v: initialValue, arr = [] }) {
557 | var origArr = arr;
558 | var idx = arr.length - 1;
559 |
560 | if (initialValue === undefined && arr.length > 0) {
561 | initialValue = arr[idx];
562 | arr = arr.slice( 0, idx );
563 | idx--;
564 | }
565 |
566 | for (let i = arr.length - 1; i >= 0; i--) {
567 | initialValue = reducerFn( {acc: initialValue, v: arr[i], i: idx--, arr: origArr} );
568 | }
569 |
570 | return initialValue;
571 | }
572 |
573 | function flatten({ v: arr = [], n: depth = Infinity }) {
574 | depth = Number( depth );
575 | var list = [];
576 |
577 | for (let v of arr) {
578 | list = list.concat(
579 | depth > 0 ?
580 | (depth > 1 && Array.isArray( v ) ?
581 | flatten( {v, n: depth - 1} ) :
582 | v
583 | ) :
584 | [v]
585 | );
586 | }
587 |
588 | return list;
589 | }
590 |
591 | function zip({ arr1 = [], arr2 = [] }) {
592 | var zipped = [];
593 | var i1 = 0;
594 | var i2 = 0;
595 |
596 | while (i1 < arr1.length && i2 < arr2.length) {
597 | zipped.push( [arr1[i1++], arr2[i2++]] );
598 | }
599 |
600 | return zipped;
601 | }
602 |
603 | function trampoline({ fn }) {
604 | return function trampolined(...args) {
605 | var result = fn( ...args );
606 |
607 | while (typeof result == "function") {
608 | result = result();
609 | }
610 |
611 | return result;
612 | };
613 | }
614 |
615 | function transduce({ fn: transducer, co: combinationFn, v: initialValue, arr = [] }) {
616 | var reducer = transducer( {v: combinationFn} );
617 |
618 | return reduce( {fn: reducer, v: initialValue, arr} );
619 | }
620 |
621 | function stdTransduce(transducer,combinationFn,initialValue,arr = []) {
622 | var reducer = transducer( combinationFn );
623 |
624 | return publicAPI.std.reduce( reducer, initialValue, arr );
625 | }
626 |
627 | function into({ fn: transducer, v: initialValue, arr = [] }) {
628 | var combinationFn =
629 | typeof initialValue == "string" ? strConcat :
630 | typeof initialValue == "number" ? numericAdd :
631 | typeof initialValue == "boolean" ? booleanAnd :
632 | Array.isArray( initialValue ) ? arrayPush :
633 | publicAPI.transducers.default;
634 |
635 | return transduce( {fn: transducer, co: combinationFn, v: initialValue, arr} );
636 | }
637 |
638 | function stdInto(transducer,initialValue,arr = []) {
639 | var combinationFn =
640 | typeof initialValue == "string" ? stdStrConcat :
641 | typeof initialValue == "number" ? stdNumericAdd :
642 | typeof initialValue == "boolean" ? stdBooleanAnd :
643 | Array.isArray( initialValue ) ? stdArrayPush :
644 | publicAPI.std.transducers.default;
645 |
646 | return stdTransduce( transducer, combinationFn, initialValue, arr );
647 | }
648 |
649 | function transducerMap(argsObj) {
650 | var { fn: mapperFn, v: combinationFn } = argsObj;
651 |
652 | // still waiting on the combination function?
653 | if (!_hasProp( argsObj, "v" ) || !combinationFn) {
654 | // Note: the combination function is usually a composed
655 | // function, so we expect the argument by itself,
656 | // not wrapped in an object
657 | return function curried({ v }){
658 | return transducerMap( {fn: mapperFn, v} );
659 | };
660 | }
661 |
662 | return function reducer({ acc, v }){
663 | return combinationFn( {acc, v: mapperFn( {v} )} );
664 | };
665 | }
666 |
667 | function stdTransducerMap(mapperFn,combinationFn) {
668 | return function reducer(acc,v){
669 | return combinationFn( acc, mapperFn( v ) );
670 | };
671 | }
672 |
673 | function transducerFilter(argsObj) {
674 | var { fn: predicateFn, v: combinationFn } = argsObj;
675 |
676 | // still waiting on the combination function?
677 | if (!_hasProp( argsObj, "v" ) || !combinationFn) {
678 | // Note: the combination function is usually a composed
679 | // function, so we expect the argument by itself,
680 | // not wrapped in an object
681 | return function curried({ v }){
682 | return transducerFilter( {fn: predicateFn, v} );
683 | };
684 | }
685 |
686 | return function reducer({ acc, v } = {}){
687 | if (predicateFn( {v} )) {
688 | return combinationFn( {acc, v} );
689 | }
690 |
691 | return acc;
692 | };
693 | }
694 |
695 | function stdTransducerFilter(predicateFn,combinationFn) {
696 | return function reducer(acc,v){
697 | if (predicateFn( v )) {
698 | return combinationFn( acc, v );
699 | }
700 |
701 | return acc;
702 | };
703 | }
704 |
705 | function strConcat({ acc, v }) {
706 | return String( acc ) + v;
707 | }
708 |
709 | function stdStrConcat(acc,v) {
710 | return String( acc ) + v;
711 | }
712 |
713 | function arrayPush({ acc, v }) {
714 | acc.push( v );
715 | return acc;
716 | }
717 |
718 | function stdArrayPush(acc,v) {
719 | acc.push( v );
720 | return acc;
721 | }
722 |
723 | function numericAdd({ acc, v }) {
724 | return (+acc) + (+v);
725 | }
726 |
727 | function stdNumericAdd(acc,v) {
728 | return (+acc) + (+v);
729 | }
730 |
731 | function booleanAnd({ acc, v }) {
732 | return !!acc && !!v;
733 | }
734 |
735 | function stdBooleanAnd(acc,v) {
736 | return !!acc && !!v;
737 | }
738 |
739 | function booleanOr({ acc, v }) {
740 | return !!acc || !!v;
741 | }
742 |
743 | function stdBooleanOr(acc,v) {
744 | return !!acc || !!v;
745 | }
746 |
747 | function stdFlip(fn) {
748 | return function flipped(arg1,arg2,...args){
749 | return fn( arg2, arg1, ...args );
750 | };
751 | }
752 |
753 | function stdReverseArgs(fn) {
754 | return function reversed(...args){
755 | return fn( ...args.reverse() );
756 | };
757 | }
758 |
759 | function head({ v = [] }) {
760 | if (v && (typeof v == "object" || typeof v == "string")) {
761 | return v[0];
762 | }
763 | return v;
764 | }
765 |
766 | function tail({ v = [] }) {
767 | if (v && (typeof v == "object" || typeof v == "string")) {
768 | if (typeof v.slice == "function") {
769 | return v.slice( 1 );
770 | }
771 | else {
772 | let props = Object.keys( v ).filter( k => k != "0" );
773 | return pick( {v, props} );
774 | }
775 | }
776 | return v;
777 | }
778 |
779 | function take({ v = [], n = 1}) {
780 | if (
781 | v &&
782 | (typeof v == "object" || typeof v == "string") &&
783 | typeof v.slice == "function"
784 | ) {
785 | return v.slice( 0, n );
786 | }
787 | return [];
788 | }
789 |
790 | // adapted from: https://github.com/caiogondim/fast-memoize.js
791 | function memoize({ fn, n = fn.length }) {
792 | var cache = {};
793 |
794 | return Number( n ) > 1 ? memoizedMultipleArgs : memoizedSingleArg;
795 |
796 |
797 | // *********************
798 |
799 | function memoizedSingleArg(arg,...otherArgs) {
800 | var hash =
801 | // arg is a primitive?
802 | (
803 | arg == null ||
804 | !(typeof arg == "object" || typeof arg == "function")
805 | ) ?
806 | arg :
807 | JSON.stringify( arg );
808 |
809 | return (hash in cache) ?
810 | cache[hash] :
811 | (cache[hash] = fn( arg, ...otherArgs ));
812 | }
813 |
814 | function memoizedMultipleArgs(...args) {
815 | var arg = args[0];
816 | var hash =
817 | // only one argument?
818 | args.length == 1 &&
819 | // arg is a primitive?
820 | (
821 | arg == null ||
822 | !(typeof arg == "object" || typeof arg == "function")
823 | ) ?
824 | arg :
825 | JSON.stringify( args );
826 |
827 | return (hash in cache) ?
828 | cache[hash] :
829 | (cache[hash] = fn( ...args ));
830 | }
831 | }
832 |
833 | function remap({ fn, args = {}}) {
834 | var props = {};
835 |
836 | // transpose `args` from `target: source` to
837 | // `source: target` for `reassoc(..)` to use
838 | for (let prop of Object.keys( args )) {
839 | props[args[prop]] = prop;
840 | }
841 |
842 | return function remapped(argsObj){
843 | return fn( reassoc( {v: argsObj, props} ) );
844 | };
845 | }
846 |
847 |
848 | // ***************************************
849 | // Private
850 |
851 | function _setMethodAlias(origName,aliasName) {
852 | publicAPI[aliasName] = publicAPI[origName];
853 | publicAPI.std[aliasName] = publicAPI.std[origName];
854 | }
855 |
856 | function _applyFnProp(fn,props) {
857 | return function fnApplied(argsObj){
858 | argsObj = Object.assign( {}, argsObj );
859 | argsObj.fn = apply( {fn: argsObj.fn, props} );
860 | return fn( argsObj );
861 | };
862 | }
863 |
864 | function _hasProp(obj,prop) {
865 | return Object.hasOwnProperty.call( obj, prop );
866 | }
867 |
868 | });
869 |
--------------------------------------------------------------------------------
/docs/core-API.md:
--------------------------------------------------------------------------------
1 | # Core API
2 |
3 | These are the methods on the `FPO.*` namespace. For the `FPO.std.*` methods, consult the [Standard API documentation](std-API.md).
4 |
5 | * [`FPO.apply(..)`](#fpoapply) (aliases: `FPO.spread(..)`)
6 | * [`FPO.binary(..)`](#fpobinary)
7 | * [`FPO.complement(..)`](#fpocomplement)
8 | * [`FPO.compose(..)`](#fpocompose) (aliases: `FPO.flowRight(..)`)
9 | * [`FPO.constant(..)`](#fpoconstant)
10 | * [`FPO.curry(..)`](#fpocurry)
11 | * [`FPO.curryMultiple(..)`](#fpocurrymultiple)
12 | * [`FPO.filterIn(..)`](#fpofilterin) (aliases: `FPO.filter(..)`, `FPO.keep(..)`)
13 | * [`FPO.filterInObj(..)`](#fpofilterinobj) (aliases: `FPO.filterObj(..)`, `FPO.keepObj(..)`)
14 | * [`FPO.filterOut(..)`](#fpofilterout) (aliases: `FPO.reject(..)`)
15 | * [`FPO.filterOutObj(..)`](#fpofilteroutobj) (aliases: `FPO.rejectObj(..)`)
16 | * [`FPO.flatMap(..)`](#fpoflatmap) (aliases: `FPO.chain(..)`)
17 | * [`FPO.flatMapObj(..)`](#fpoflatmapobj) (aliases: `FPO.chainObj(..)`)
18 | * [`FPO.flatten(..)`](#fpoflatten)
19 | * [`FPO.head(..)`](#fpohead)
20 | * [`FPO.identity(..)`](#fpoidentity)
21 | * [`FPO.map(..)`](#fpomap)
22 | * [`FPO.mapObj(..)`](#fpomapobj)
23 | * [`FPO.memoize(..)`](#fpomemoize)
24 | * [`FPO.nAry(..)`](#fponary)
25 | * [`FPO.partial(..)`](#fpopartial) (aliases: `FPO.partialRight(..)`)
26 | * [`FPO.pick(..)`](#fpopick)
27 | * [`FPO.pickAll(..)`](#fpopickall)
28 | * [`FPO.pipe(..)`](#fpopipe) (aliases: `FPO.flow(..)`, `FPO.sequence(..)`)
29 | * [`FPO.prop(..)`](#fpoprop)
30 | * [`FPO.reassoc(..)`](#fporeassoc)
31 | * [`FPO.reduce(..)`](#fporeduce) (aliases: `FPO.fold(..)`, `FPO.foldL(..)`)
32 | * [`FPO.reduceObj(..)`](#fporeduceobj) (aliases: `FPO.foldObj(..)`)
33 | * [`FPO.reduceRight(..)`](#fporeduceright) (aliases: `FPO.foldR(..)`)
34 | * [`FPO.remap(..)`](#fporemap)
35 | * [`FPO.setProp(..)`](#fposetprop) (aliases: `FPO.assoc(..)`)
36 | * [`FPO.tail(..)`](#fpotail)
37 | * [`FPO.take(..)`](#fpotake)
38 | * [`FPO.trampoline(..)`](#fpotrampoline)
39 | * **Transducers**:
40 | - [`FPO.transducers.array(..)`](#fpotransducersarray)
41 | - [`FPO.transducers.booleanAnd(..)`](#fpotransducersbooleanand) (aliases: `FPO.transducers.boolean(..)`)
42 | - [`FPO.transducers.booleanOr(..)`](#fpotransducersbooleanor)
43 | - [`FPO.transducers.default(..)`](#fpotransducersdefault)
44 | - [`FPO.transducers.filter(..)`](#fpotransducersfilter)
45 | - [`FPO.transducers.into(..)`](#fpotransducersinto)
46 | - [`FPO.transducers.map(..)`](#fpotransducersmap)
47 | - [`FPO.transducers.number(..)`](#fpotransducersnumber)
48 | - [`FPO.transducers.string(..)`](#fpotransducersstring)
49 | - [`FPO.transducers.transduce(..)`](#fpotransducerstransduce)
50 | * [`FPO.unapply(..)`](#fpounapply) (aliases: `FPO.gather(..)`)
51 | * [`FPO.unary(..)`](#fpounary)
52 | * [`FPO.uncurry(..)`](#fpouncurry)
53 | * [`FPO.zip(..)`](#fpozip)
54 |
55 | ----
56 |
57 | ### `FPO.apply(..)`
58 |
59 | ([back to top](#core-api))
60 |
61 | Wraps a function to spread out the properties from an object argument as individual positional arguments.
62 |
63 | * **Arguments:**
64 | - `fn`: function to wrap
65 | - `props`: (optional) list of property names (strings) to indicate the order to spread properties as individual arguments. If omitted, the signature of `fn` is parsed for its parameter list to try to determine an ordered property list; this detection only works for simple parameters (including those with default parameter value settings).
66 |
67 | * **Returns:** *function*
68 |
69 | * **Example:**
70 |
71 | ```js
72 | function foo(x,y = 2) { return x + y; }
73 | function bar([a,b],c) { return a + b + c; }
74 |
75 | var f = FPO.apply( {fn: foo} );
76 | var p = FPO.apply( {fn: bar, props:["x","y"]} );
77 |
78 | f( {x: 1, y: 1} ); // 2
79 | f( {x: 3} ); // 5
80 | p( {x: [1,2], y: 3} )); // 6
81 | ```
82 |
83 | * **Aliases:** `FPO.spread(..)`
84 |
85 | * **See Also:** [`FPO.unapply(..)`](#fpounapply)
86 |
87 | ----
88 |
89 | ### `FPO.binary(..)`
90 |
91 | ([back to top](#core-api))
92 |
93 | Wraps a function to restrict its inputs to only two named arguments as specified.
94 |
95 | * **Arguments:**
96 | - `fn`: function to wrap
97 | - `props`: array of two property names to allow as named arguments
98 |
99 | * **Returns:** *function*
100 |
101 | * **Example:**
102 |
103 | ```js
104 | function foo(argsObj) { return argsObj; }
105 |
106 | var f = FPO.binary( {fn: foo, props: ["x","y"]} );
107 |
108 | f( {x: 1, y: 2, z: 3} );
109 | // { x:1, y:2 }
110 | ```
111 |
112 | * **See Also:** [`FPO.nAry(..)`](#fponary), [`FPO.unary(..)`](#fpounary)
113 |
114 | ----
115 |
116 | ### `FPO.complement(..)`
117 |
118 | ([back to top](#core-api))
119 |
120 | Wraps a predicate function -- a function that produces `true` / `false` -- to negate its result.
121 |
122 | * **Arguments:**
123 | - `fn`: function to wrap
124 |
125 | * **Returns:** *function*
126 |
127 | * **Example:**
128 |
129 | ```js
130 | var f = FPO.complement( {fn: () => false} );
131 |
132 | f(); // true
133 | ```
134 |
135 | ----
136 |
137 | ### `FPO.compose(..)`
138 |
139 | ([back to top](#core-api))
140 |
141 | Produces a new function that's the composition of a list of functions. Functions are composed right-to-left (unlike [`FPO.pipe(..)`](#fpopipe)) from the array.
142 |
143 | * **Arguments:**
144 | - `fns`: array of functions
145 |
146 | * **Returns:** *function*
147 |
148 | * **Example:**
149 |
150 | ```js
151 | var f = FPO.compose( {fns: [
152 | ({v}) => v + "3",
153 | ({v}) => v + "2",
154 | ({v}) => v + "1"
155 | ]} );
156 |
157 | f( {v: "0"} ); // "0123"
158 | ```
159 |
160 | * **Aliases:** `FPO.flowRight(..)`
161 |
162 | * **See Also:** [`FPO.pipe(..)`](#fpopipe)
163 |
164 | ----
165 |
166 | ### `FPO.constant(..)`
167 |
168 | ([back to top](#core-api))
169 |
170 | Wraps a value in a function that returns the value.
171 |
172 | * **Arguments:**
173 | - `v`: value to wrap
174 |
175 | * **Returns:** *function*
176 |
177 | * **Example:**
178 |
179 | ```js
180 | var f = FPO.constant( {v: 42} );
181 |
182 | f(); // 42
183 | ```
184 |
185 | * **See Also:** [`FPO.identity(..)`](#fpoidentity)
186 |
187 | ----
188 |
189 | ### `FPO.curry(..)`
190 |
191 | ([back to top](#core-api))
192 |
193 | Curries a function so that you can pass one argument at a time, each time getting back another function to receive the next argument. Once all arguments are passed, the underlying function is called with the arguments.
194 |
195 | Unlike [`FPO.curryMultiple(..)`](#fpocurrymultiple), you can only pass one property argument at a time to each curried function (see example below). If multiple properties are passed to a curried call, only the first property (in enumeration order) will be passed.
196 |
197 | **Note:** Enumeration order of properties is not strictly guaranteed cross-environment. However, it's generally reliable as the order that properties were listed/added to the object in its definition.
198 |
199 | * **Arguments:**
200 | - `fn`: function to curry
201 | - `n`: number of arguments to curry for
202 |
203 | * **Returns:** *function*
204 |
205 | * **Example:**
206 |
207 | ```js
208 | function foo({ x,y,z }) { return x + y + z; }
209 |
210 | var f = FPO.curry( {fn: foo, n: 3} );
211 |
212 | f( {y: "b" } )()( {} )()( {x: "a", z: "!"} )( {z: "c"} );
213 | // "abc"
214 | ```
215 |
216 | * **See Also:** [`FPO.curryMultiple(..)`](#fpocurrymultiple), [`FPO.partial(..)`](#fpopartial), [`FPO.uncurry(..)`](#fpouncurry)
217 |
218 | ----
219 |
220 | ### `FPO.curryMultiple(..)`
221 |
222 | ([back to top](#core-api))
223 |
224 | Just like [`FPO.curry(..)`](#fpocurry), except each curried function allows multiple arguments instead of just one.
225 |
226 | * **Arguments:**
227 | - `fn`: function to curry
228 | - `n`: number of arguments to curry for
229 |
230 | * **Returns:** *function*
231 |
232 | * **Example:**
233 |
234 | ```js
235 | function foo({ x,y,z }) { return x + y + z; }
236 |
237 | var f = FPO.curry( {fn: foo, n: 3} );
238 |
239 | f( {y: "b" } )()( {} )()( {x: "a", z: "!"} );
240 | // "ab!"
241 | ```
242 |
243 | * **See Also:** [`FPO.curry(..)`](#fpocurry), [`FPO.partial(..)`](#fpopartial), [`FPO.uncurry(..)`](#fpouncurry)
244 |
245 | ----
246 |
247 | ### `FPO.filterIn(..)`
248 |
249 | ([back to top](#core-api))
250 |
251 | Commonly known as `filter(..)`, produces a new list by calling a predicate function with each value in the original list. For each value, if the predicate function returns true (or truthy), the value is included in (aka, filtered into) the new list. Otherwise, the value is omitted.
252 |
253 | * **Arguments:**
254 | - `fn`: predicate function; called with `v` (value), `i` (index), and `arr` (array) named arguments
255 | - `arr`: array to filter against
256 |
257 | * **Returns:** *array*
258 |
259 | * **Example:**
260 |
261 | ```js
262 | function isOdd({ v }) { return v % 2 == 1; }
263 |
264 | var nums = [1,2,3,4,5];
265 |
266 | FPO.filterIn( {fn: isOdd, arr: nums} );
267 | // [1,3,5]
268 | ```
269 |
270 | * **Aliases:** `FPO.filter(..)`, `FPO.keep(..)`
271 |
272 | * **See Also:** [`FPO.filterInObj(..)`](#fpofilterinobj), [`FPO.filterOut(..)`](#fpofilterout)
273 |
274 | ----
275 |
276 | ### `FPO.filterInObj(..)`
277 |
278 | ([back to top](#core-api))
279 |
280 | Produces a new object by calling a predicate function with each property value in the original object. For each value, if the predicate function returns true (or truthy), the value is included in (aka, filtered into) the new object at the same property name. Otherwise, the value is omitted.
281 |
282 | * **Arguments:**
283 | - `fn`: predicate function; called with `v` (value), `i` (property name), and `o` (object) named arguments
284 | - `o`: object to filter against
285 |
286 | * **Returns:** *object*
287 |
288 | * **Example:**
289 |
290 | ```js
291 | function isOdd({ v }) { return v % 2 == 1; }
292 |
293 | var nums = {a: 1, b: 2, c: 3, d: 4, e: 5};
294 |
295 | FPO.filterInObj( {fn: isOdd, o: nums} );
296 | // {a: 1, c: 3, e: 5}
297 | ```
298 |
299 | * **Aliases:** `FPO.filterObj(..)`, `FPO.keepObj(..)`
300 |
301 | * **See Also:** [`FPO.filterIn(..)`](#fpofilterin), [`FPO.filterOutObj(..)`](#fpofilteroutobj)
302 |
303 | ----
304 |
305 | ### `FPO.filterOut(..)`
306 |
307 | ([back to top](#core-api))
308 |
309 | The inverse of [`FPO.filterIn(..)`](#fpofilterin), produces a new list by calling a predicate function with each value in the original list. For each value, if the predicate function returns true (or truthy), the value is omitted from (aka, filtered out of) the new list. Otherwise, the value is included.
310 |
311 | * **Arguments:**
312 | - `fn`: predicate function; called with `v` (value), `i` (index), and `arr` (array) named arguments
313 | - `arr`: array to filter against
314 |
315 | * **Returns:** *array*
316 |
317 | * **Example:**
318 |
319 | ```js
320 | function isOdd({ v }) { return v % 2 == 1; }
321 |
322 | var nums = [1,2,3,4,5];
323 |
324 | FPO.filterOut( {fn: isOdd, arr: nums} );
325 | // [2,4]
326 | ```
327 |
328 | * **Aliases:** `FPO.reject(..)`
329 |
330 | * **See Also:** [`FPO.filterOutObj(..)`](#fpofilteroutobj), [`FPO.filterIn(..)`](#fpofilterin)
331 |
332 | ----
333 |
334 | ### `FPO.filterOutObj(..)`
335 |
336 | ([back to top](#core-api))
337 |
338 | The inverse of [`FPO.filterInObj(..)`](#fpofilterinobj), produces a new object by calling a predicate function with each property value in the original object. For each value, if the predicate function returns true (or truthy), the value is omitted from (aka, filtered out of) the new object. Otherwise, the value is included at the same property name.
339 |
340 | * **Arguments:**
341 | - `fn`: predicate function; called with `v` (value), `i` (property name), and `o` (object) named arguments
342 | - `o`: object to filter against
343 |
344 | * **Returns:** *object*
345 |
346 | * **Example:**
347 |
348 | ```js
349 | function isOdd({ v }) { return v % 2 == 1; }
350 |
351 | var nums = {a: 1, b: 2, c: 3, d: 4, e: 5};
352 |
353 | FPO.filterOutObj( {fn: isOdd, o: nums} );
354 | // {b: 2, d: 4}
355 | ```
356 |
357 | * **Aliases:** `FPO.rejectObj(..)`
358 |
359 | * **See Also:** [`FPO.filterOut(..)`](#fpofilterout), [`FPO.filterInObj(..)`](#fpofilterinobj)
360 |
361 | ----
362 |
363 | ### `FPO.flatMap(..)`
364 |
365 | ([back to top](#core-api))
366 |
367 | Similar to [`FPO.map(..)`](#fpomap), produces a new list by calling a mapper function with each value in the original list. If the mapper function returns an array, this array is flattened (one level) into the overall array.
368 |
369 | * **Arguments:**
370 | - `fn`: mapper function; called with `v` (value), `i` (index), and `arr` (array) named arguments
371 | - `arr`: array to flat-map against
372 |
373 | * **Returns:** *array*
374 |
375 | * **Example:**
376 |
377 | ```js
378 | function splitChars({ v }) { return [...v]; }
379 |
380 | var words = ["hello","world"];
381 |
382 | splitChars( {v: words[0]} );
383 | // ["h","e","l","l","o"]
384 |
385 | FPO.map( {fn: splitChars, arr: words} );
386 | // [["h","e","l","l","o"],["w","o","r","l","d"]]
387 |
388 | FPO.flatMap( {fn: splitChars, arr: words} );
389 | // ["h","e","l","l","o","w","o","r","l","d"]
390 | ```
391 |
392 | * **Aliases:** `FPO.chain(..)`
393 |
394 | * **See Also:** [`FPO.flatMapObj(..)`](#fpoflatmapobj), [`FPO.map(..)`](#fpomap), [`FPO.flatten(..)`](#fpoflatten)
395 |
396 | ----
397 |
398 | ### `FPO.flatMapObj(..)`
399 |
400 | ([back to top](#core-api))
401 |
402 | Similar to [`FPO.mapObj(..)`](#fpomapobj), produces a new object by calling a mapper function with each property value in the original object. If the mapper function returns an object, this object is flattened (one level) into the overall object, by copying its properties.
403 |
404 | * **Arguments:**
405 | - `fn`: mapper function; called with `v` (value), `i` (property name), and `o` (object) named arguments
406 | - `o`: object to flat-map against
407 |
408 | * **Returns:** *object*
409 |
410 | * **Example:**
411 |
412 | ```js
413 | function splitEvensInHalf({ v, i }) {
414 | if (v % 2 == 0) {
415 | return { [i]: v/2, [i+"_2"]: v/2 };
416 | }
417 | return v;
418 | }
419 |
420 | var nums = {a: 1, b: 2, c: 3, d: 4};
421 |
422 | splitEvensInHalf( {v: 3, i: "c"} );
423 | // 3
424 |
425 | splitEvensInHalf( {v: 4, i: "d"} );
426 | // {d: 2, d_2: 2}
427 |
428 | FPO.mapObj( {fn: splitEvensInHalf, o: nums} );
429 | // {a: 1, b: {b: 1, b_2: 1}, c: 3, d: {d: 2, d_2: 2}}
430 |
431 | FPO.flatMapObj( {fn: splitEvensInHalf, o: nums} );
432 | // {a: 1, b: 1, b_2: 1, c: 3, d: 2, d_2: 2};
433 | ```
434 |
435 | * **Aliases:** `FPO.chainObj(..)`
436 |
437 | * **See Also:** [`FPO.flatMap(..)`](#fpoflatmap), [`FPO.mapObj(..)`](#fpomapobj)
438 |
439 | ----
440 |
441 | ### `FPO.flatten(..)`
442 |
443 | ([back to top](#core-api))
444 |
445 | Flattens an array of nested arrays. Optionally, specify how many levels of nesting to flatten out.
446 |
447 | * **Arguments:**
448 | - `v`: array to flat-map against
449 | - `n`: (optional) the number of levels of nesting to flatten out; if omitted, defaults to Infinity (to flatten any nested depth).
450 |
451 | * **Returns:** *array*
452 |
453 | * **Example:**
454 |
455 | ```js
456 | var nums = [1,2,[3,4],[5,[6,7]]];
457 |
458 | FPO.flatten( {v: nums} );
459 | // [1,2,3,4,5,6,7]
460 |
461 | FPO.flatten( {v: nums, n: 1} );
462 | // [1,2,3,4,5,[6,7]]
463 |
464 | FPO.flatten( {v: nums, n: 2} );
465 | // [1,2,3,4,5,6,7]
466 | ```
467 |
468 | * **See Also:** [`FPO.flatMap(..)`](#fpoflatmap)
469 |
470 | ----
471 |
472 | ### `FPO.head(..)`
473 |
474 | ([back to top](#core-api))
475 |
476 | Returns the element as accessed at index 0 of the value.
477 |
478 | * **Arguments:**
479 | - `v`: array, string, object
480 |
481 | * **Returns:** *any*
482 |
483 | * **Example:**
484 |
485 | ```js
486 | var nums = [1,2,3,4];
487 |
488 | FPO.head( {v: nums} );
489 | // 1
490 |
491 | FPO.head( {v: []} );
492 | // undefined
493 |
494 | FPO.head( {v: "abc"} );
495 | // "a"
496 |
497 | FPO.head( {v: {0: 42}} );
498 | // 42
499 | ```
500 |
501 | * **See Also:** [`FPO.tail(..)`](#fpotail)
502 |
503 | ----
504 |
505 | ### `FPO.identity(..)`
506 |
507 | ([back to top](#core-api))
508 |
509 | Returns the value given to it. Useful as a default placeholder for certain opertaions (i.e., composition, reduction).
510 |
511 | * **Arguments:**
512 | - `v`: value to return
513 |
514 | * **Returns:** *-any-*
515 |
516 | * **Example:**
517 |
518 | ```js
519 | FPO.identity( {v: 42} ); // 42
520 | ```
521 |
522 | * **See Also:** [`FPO.constant(..)`](#fpoconstant)
523 |
524 | ----
525 |
526 | ### `FPO.map(..)`
527 |
528 | ([back to top](#core-api))
529 |
530 | Produces a new list by calling a mapper function with each value in the original list. The value the mapper function returns is inserted in the new list at that same position. The new list will always be the same length as the original list.
531 |
532 | * **Arguments:**
533 | - `fn`: mapper function; called with `v` (value), `i` (index), and `arr` (array) named arguments
534 | - `arr`: array to map against
535 |
536 | * **Returns:** *array*
537 |
538 | * **Example:**
539 |
540 | ```js
541 | function double({ v }) { return v * 2; }
542 |
543 | var nums = [1,2,3,4,5];
544 |
545 | FPO.map( {fn: double, arr: nums} );
546 | // [2,4,6,8,10]
547 | ```
548 |
549 | * **See Also:** [`FPO.mapObj(..)`](#fpomapobj), [`FPO.flatMap(..)`](#fpoflatmap)
550 |
551 | ----
552 |
553 | ### `FPO.mapObj(..)`
554 |
555 | ([back to top](#core-api))
556 |
557 | Produces a new object by calling a mapper function with each property value in the original object. The value the mapper function returns is inserted in the new object at that same property name. The new object will always have the same number of properties as the original object.
558 |
559 | * **Arguments:**
560 | - `fn`: mapper function; called with `v` (value), `i` (property name), and `o` (object) named arguments
561 | - `o`: object to map against
562 |
563 | * **Returns:** *object*
564 |
565 | * **Example:**
566 |
567 | ```js
568 | function double({ v }) { return v * 2; }
569 |
570 | var nums = {a: 1, b: 2, c: 3, d: 4, e: 5};
571 |
572 | FPO.mapObj( {fn: double, o: nums} );
573 | // {a: 2, b: 4, c: 6, d: 8, e: 10};
574 | ```
575 |
576 | * **See Also:** [`FPO.map(..)`](#fpomap)
577 |
578 | ----
579 |
580 | ### `FPO.memoize(..)`
581 |
582 | ([back to top](#core-api))
583 |
584 | For performance optimization reasons, wraps a function such that it remembers each set of arguments passed to it, associated with that underlying return value. If the wrapped function is called subsequent times with the same set of arguments, the cached return value is returned instead of being recomputed. Each wrapped function instance has its own separate cache, even if wrapping the same original function multiple times.
585 |
586 | A set of arguments is "remembered" by being hashed to a string value to use as a cache key. This hashing is done internally with `JSON.stringify(..)`, which is fast and works with many common JS value types. However, this hashing is by no means bullet-proof for all types, and does not guarantee collision-free. **Use caution:** generally, you should only use primitives (number, string, boolean, null, and undefined) or simple objects (object, array) as arguments. If you use objects, always make sure to list properties in the same order to ensure proper hashing.
587 |
588 | By default, the function's arity (`fn.length`) will be detected as `n`. However, in JS certain techniques thwart this detection, such as the use of default parameters or parameter destructuring. Make sure to specify the correct `n` if detection is uncertain or unreliable.
589 |
590 | Unary functions (single argument; `n` of `1`) with a primitive argument are the fastest for memoization, so if possible, try to design functions that way. In these cases, specifying `n` as `1` will help ensure the best possible performance.
591 |
592 | **Warning:** Be aware that if `1` is initially specified (or detected) for `n`, additional arguments later passed to the wrapped function are **not considered** in the memoization hashing, though they will still be passed to the underlying function as-is. This may cause unexpected results (false-positives on cache hits); always make sure `n` matches the expected number of arguments.
593 |
594 | * **Arguments:**
595 | - `fn`: function to wrap
596 | - `n`: number of arguments to memoize; if omitted, tries to detect the arity (`fn.length`) to use.
597 |
598 | * **Returns:** *array*
599 |
600 | * **Example:**
601 |
602 | ```js
603 | function sum(x,y) { console.log( "sum called!" ); return x + y; }
604 | function mult({x, y}) { console.log( "mult called!" ); return x * y; }
605 |
606 | var A = FPO.memoize( {fn: sum} );
607 | var B = FPO.memoize( {fn: sum, n: 1} ); // be careful here!
608 | var C = FPO.memoize( {fn: mult, n: 1} );
609 |
610 | A( 2, 3 );
611 | // sum called!
612 | // 5
613 |
614 | A( 2, 3 ); // no need to re-compute, value pulled from cache
615 | // 5
616 |
617 | B( 2, 3 ); // different instance, separate cache, re-computed
618 | // sum called!
619 | // 5
620 |
621 | B( 2, 100 ); // oops, memoization fail here!
622 | // 5
623 |
624 | C( {x: 3, y: 4} );
625 | // mult called!
626 | // 12
627 |
628 | C( {x: 3, y: 4} );
629 | // 12
630 |
631 | C( {y: 4, x: 3} ); // oops, cache hashing is different
632 | // mult called!
633 | // 12
634 | ```
635 |
636 | ----
637 |
638 | ### `FPO.nAry(..)`
639 |
640 | ([back to top](#core-api))
641 |
642 | Wraps a function to restrict its inputs to only the named arguments as specified.
643 |
644 | * **Arguments:**
645 | - `fn`: function to wrap
646 | - `props`: array of property names to allow as named arguments; if empty, produces a "nullary" function -- won't receive any arguments.
647 |
648 | * **Returns:** *function*
649 |
650 | * **Example:**
651 |
652 | ```js
653 | function foo(argsObj) { return argsObj; }
654 |
655 | var f = FPO.nAry( {fn: foo, props: ["x","y","z"]} );
656 |
657 | f( {x: 1, y: 2, z: 3, w: 4} );
658 | // { x:1, y:2, z:3 }
659 | ```
660 |
661 | * **See Also:** [`FPO.binary(..)`](#fpobinary), [`FPO.unary(..)`](#fpounary)
662 |
663 | ----
664 |
665 | ### `FPO.partial(..)`
666 |
667 | ([back to top](#core-api))
668 |
669 | Wraps a function with a new function that already has some of the arguments pre-specified, and is waiting for the rest of them on the next call. Unlike [`FPO.curry(..)`](#fpocurry), you must specify all the remaining arguments on the next call of the partially-applied function.
670 |
671 | With traditional FP libraries, `partial(..)` works in left-to-right order (as does `FPO.std.partial(..)`). That's why typically you also need a `FPO.std.partialRight(..)` if you want to partially-apply from the opposite direction.
672 |
673 | However, using named arguments style -- after all, that is the whole point of FPO! -- order doesn't matter. For familiarity sake, `FPO.partialRight(..)` is provided, but it's just an alias to `FPO.partial(..)`.
674 |
675 | * **Arguments:**
676 | - `fn`: function to partially-apply
677 | - `args`: object containing the arguments to apply now
678 |
679 | * **Returns:** *function*
680 |
681 | * **Example:**
682 |
683 | ```js
684 | function foo({ x,y,z }) { return x + y + z; }
685 |
686 | var f = FPO.partial( {fn: foo, args: {x: "a"}} );
687 |
688 | f( {y: "b", z: "!"} );
689 | // "ab!"
690 | ```
691 |
692 | * **Aliases:** `FPO.partialRight(..)`
693 |
694 | * **See Also:** [`FPO.curry(..)`](#fpocurry), [`FPO.curryMultiple(..)`](#fpocurrymultiple)
695 |
696 | ----
697 |
698 | ### `FPO.pick(..)`
699 |
700 | ([back to top](#core-api))
701 |
702 | Returns a new object with only the specified properties from the original object. Includes only properties from the original object.
703 |
704 | * **Arguments:**
705 | - `v`: object to pick properties from
706 | - `props`: array of property names to pick from the object; if a property does not exist on the original object, it **is not** added to the new object, unlike [`FPO.pickAll(..)`](#fpopickall).
707 |
708 | * **Returns:** *object*
709 |
710 | * **Example:**
711 |
712 | ```js
713 | var obj = { x: 1, y: 2, z: 3 };
714 |
715 | FPO.pick( {v: obj, props: ["x","y","w"]} );
716 | // { x:1, y:2 }
717 | ```
718 |
719 | * **See Also:** [`FPO.pickAll(..)`](#fpopickall), [`FPO.prop(..)`](#fpoprop)
720 |
721 | ----
722 |
723 | ### `FPO.pickAll(..)`
724 |
725 | ([back to top](#core-api))
726 |
727 | Returns a new object with only the specified properties from the original object. Includes all specified properties.
728 |
729 | * **Arguments:**
730 | - `v`: object to pick properties from
731 | - `props`: array of property names to pick from the object; even if a property does not exist on the original object, it **is** still added to the new object with an `undefined` value, unlike [`FPO.pick(..)`](#fpopick).
732 |
733 | * **Returns:** *object*
734 |
735 | * **Example:**
736 |
737 | ```js
738 | var obj = { x: 1, y: 2, z: 3 };
739 |
740 | FPO.pickAll( {v: obj, props: ["x","y","w"]} );
741 | // { x:1, y:2, w:undefined }
742 | ```
743 |
744 | * **See Also:** [`FPO.pick(..)`](#fpopick)
745 |
746 | ----
747 |
748 | ### `FPO.pipe(..)`
749 |
750 | ([back to top](#core-api))
751 |
752 | Produces a new function that's the composition of a list of functions. Functions are composed left-to-right (unlike [`FPO.compose(..)`](#fpocompose)) from the array.
753 |
754 | * **Arguments:**
755 | - `fns`: array of functions
756 |
757 | * **Returns:** *function*
758 |
759 | * **Example:**
760 |
761 | ```js
762 | var f = FPO.pipe( {fns: [
763 | ({v}) => v + "3",
764 | ({v}) => v + "2",
765 | ({v}) => v + "1"
766 | ]} );
767 |
768 | f( {v: "4"} ); // "4321"
769 | ```
770 |
771 | * **Aliases:** `FPO.flow(..)`, `FPO.sequence(..)`
772 |
773 | * **See Also:** [`FPO.compose(..)`](#fpocompose)
774 |
775 | ----
776 |
777 | ### `FPO.prop(..)`
778 |
779 | ([back to top](#core-api))
780 |
781 | Extracts a property's value from an object.
782 |
783 | * **Arguments:**
784 | - `v`: object to pull the property value from
785 | - `prop`: property name to pull from the object
786 |
787 | * **Returns:** *-any-*
788 |
789 | * **Example:**
790 |
791 | ```js
792 | var obj = { x: 1, y: 2, z: 3 };
793 |
794 | FPO.prop( {v: obj, prop: "y"} ); // 2
795 | ```
796 |
797 | * **See Also:** [`FPO.pick(..)`](#fpopick), [`FPO.setProp(..)`](#fposetprop)
798 |
799 | ----
800 |
801 | ### `FPO.reassoc(..)`
802 |
803 | ([back to top](#core-api))
804 |
805 | Like a mixture between [`FPO.pick(..)`](#fpopick) and [`FPO.setProp(..)`](#fposetprop), creates a new object that has properties remapped from original names to new names. Any properties present on the original object that aren't remapped are copied with the same name.
806 |
807 | * **Arguments:**
808 | - `props`: object whose key/value pairs are `sourceProp: targetProp` remappings
809 | - `v`: object to remap properties from
810 |
811 | * **Returns:** *object*
812 |
813 | * **Example:**
814 |
815 | ```js
816 | var obj = { x: 1, y: 2, z: 3 };
817 |
818 | FPO.reassoc( {v: obj, props: {x: "A", z: "__z__"}} );
819 | // { A: 1, __z__: 3, y: 2}
820 | ```
821 |
822 | * **See Also:** [`FPO.pick(..)`](#fpopick), [`FPO.setProp(..)`](#fposetprop)
823 |
824 | ----
825 |
826 | ### `FPO.reduce(..)`
827 |
828 | ([back to top](#core-api))
829 |
830 | Processes a list from left-to-right (unlike [`FPO.reduceRight(..)`](#fporeduceright)), successively combining (aka "reducing", "folding") two values into one, until the entire list has been reduced to a single value. An initial value for the reduction can optionally be provided.
831 |
832 | * **Arguments:**
833 | - `fn`: reducer function; called with `acc` (accumulator), `v` (value), `i` (index), and `arr` (array) named arguments
834 | - `arr`: array to reduce
835 | - `v`: (optional) initial value to use for the reduction; if provided, the first reduction will pass to the reducer the initial value as the `acc` and the first value from the array as `v`. Otherwise, the first reduction has the first value of the array as `acc` and the second value of the array as `v`.
836 |
837 | * **Returns:** *-any-*
838 |
839 | * **Example:**
840 |
841 | ```js
842 | function strConcat({ acc, v }) { return acc + v; }
843 |
844 | var vowels = ["a","e","i","o","u","y"];
845 |
846 | FPO.reduce( {fn: strConcat, arr: vowels} );
847 | // "aeiouy"
848 |
849 | FPO.reduce( {fn: strConcat, arr: vowels, v: "vowels: "} );
850 | // "vowels: aeiouy"
851 | ```
852 |
853 | * **Aliases:** `FPO.fold(..)`, `FPO.foldL(..)`
854 |
855 | * **See Also:** [`FPO.reduceObj(..)`](#fporeduceobj), [`FPO.reduceRight(..)`](#fporeduceright)
856 |
857 | ----
858 |
859 | ### `FPO.reduceObj(..)`
860 |
861 | ([back to top](#core-api))
862 |
863 | Processes an object's properties (in enumeration order), successively combining (aka "reducing", "folding") two values into one, until all the object's properties have been reduced to a single value. An initial value for the reduction can optionally be provided.
864 |
865 | **Note:** Enumeration order of properties is not strictly guaranteed cross-environment. However, it's generally reliable as the order that properties were listed/added to the object in its definition.
866 |
867 | * **Arguments:**
868 | - `fn`: reducer function; called with `acc` (accumulator), `v` (value), `i` (property name), and `o` (object) named arguments
869 | - `o`: object to reduce
870 | - `v`: (optional) initial value to use for the reduction; if provided, the first reduction will pass to the reducer the initial value as the `acc` and the first property value (in enumeration order) from the object as `v`. Otherwise, the first reduction has the first property value (in enumeration order) of the object as `acc` and the second property value (in enumeration order) of the object as `v`.
871 |
872 | * **Returns:** *-any-*
873 |
874 | * **Example:**
875 |
876 | ```js
877 | function strConcat({ acc, v }) { return acc + v; }
878 |
879 | var vowels = {a: "a", b: "e", c: "i", d: "o", e: "u", f: "y"};
880 |
881 | FPO.reduceObj( {fn: strConcat, o: vowels} );
882 | // "aeiouy"
883 |
884 | FPO.reduceObj( {fn: strConcat, o: vowels, v: "vowels: "} );
885 | // "vowels: aeiouy"
886 | ```
887 |
888 | * **Aliases:** `FPO.foldObj(..)`
889 |
890 | * **See Also:** [`FPO.reduce(..)`](#fporeduce)
891 |
892 | ----
893 |
894 | ### `FPO.reduceRight(..)`
895 |
896 | ([back to top](#core-api))
897 |
898 | Processes a list from right-to-left (unlike [`FPO.reduce(..)`](#fporeduce)), successively combining (aka "reducing", "folding") two values into one, until the entire list has been reduced to a single value.
899 |
900 | An initial value for the reduction can optionally be provided. If the array is empty, the initial value is returned (or `undefined` if it was omitted).
901 |
902 | * **Arguments:**
903 | - `fn`: reducer function; called with `acc` (accumulator), `v` (value), `i` (index), and `arr` (array) named arguments
904 | - `arr`: array to reduce
905 | - `v`: (optional) initial value to use for the reduction; if provided, the first reduction will pass to the reducer the initial value as the `acc` and the first value from the array as `v`. Otherwise, the first reduction has the first value of the array as `acc` and the second value of the array as `v`.
906 |
907 | * **Returns:** *-any-*
908 |
909 | * **Example:**
910 |
911 | ```js
912 | function strConcat({ acc, v }) { return acc + v; }
913 |
914 | var vowels = ["a","e","i","o","u","y"];
915 |
916 | FPO.reduceRight( {fn: strConcat, arr: vowels} );
917 | // "yuoiea"
918 |
919 | FPO.reduceRight( {fn: strConcat, arr: vowels, v: "vowels: "} );
920 | // "vowels: yuoiea"
921 | ```
922 |
923 | * **Aliases:** `FPO.foldR(..)`
924 |
925 | * **See Also:** [`FPO.reduce(..)`](#fporeduce)
926 |
927 | ----
928 |
929 | ### `FPO.remap(..)`
930 |
931 | ([back to top](#core-api))
932 |
933 | Remaps the expected named arguments of a function. This is useful to adapt a function to be used if the arguments passed in will be different than what the function expects.
934 |
935 | A common usecase will be to adapt a function so it's suitable for use as a mapper/predicate/reducer function, or for composition.
936 |
937 | * **Arguments:**
938 | - `fn`: function to remap
939 | - `args`: object whose key/value pairs represent the `origArgName: newArgName` mappings
940 |
941 | * **Returns:** *function*
942 |
943 | * **Example:**
944 |
945 | ```js
946 | function double({ x }) { return x * 2; }
947 | function increment({ y }) { return y + 1; }
948 | function div3({ z }) { return z / 3; }
949 |
950 | var f = FPO.remap( {fn: double, args: {x: "v"}} );
951 | var g = FPO.remap( {fn: increment, args: {y: "v"}} );
952 | var h = FPO.remap( {fn: div3, args: {z: "v"}} );
953 |
954 | f( {v: 3} );
955 | // 6
956 |
957 | FPO.map( {fn: g, arr: [5,10,15,20,25]} );
958 | // [6,11,16,21,26]
959 |
960 | var m = FPO.compose( {fns: [h,g,f]} );
961 | m( {v: 4} );
962 | // 3
963 |
964 | FPO.map( {fn: m, arr: [1,4,7,10,13]} );
965 | // [1,3,5,7,9]
966 | ```
967 |
968 | * **See Also:** [`FPO.reassoc(..)`](#fporeassoc)
969 |
970 | ----
971 |
972 | ### `FPO.setProp(..)`
973 |
974 | ([back to top](#core-api))
975 |
976 | Creates a shallow clone of an object, assigning the specified property value to the new object.
977 |
978 | * **Arguments:**
979 | - `o`: (optional) object to clone; if omitted, defaults to a new empty object
980 | - `prop`: property name where to set the value on the new object
981 | - `v`: value
982 |
983 | * **Returns:** *object*
984 |
985 | * **Example:**
986 |
987 | ```js
988 | var obj = { x: 1, y: 2, z: 3 };
989 |
990 | FPO.setProp( {o: obj, prop: "w", v: 4} );
991 | // { x:1, y:2, z:3, w:4 }
992 |
993 | obj;
994 | // { x:1, y:2, z:3 }
995 | ```
996 |
997 | * **Aliases:** `FPO.assoc(..)`
998 |
999 | * **See Also:** [`FPO.prop(..)`](#fpoprop)
1000 |
1001 | ----
1002 |
1003 | ### `FPO.tail(..)`
1004 |
1005 | ([back to top](#core-api))
1006 |
1007 | Returns everything else in the value except the element as accessed at index 0; basically the inverse of [`FPO.head(..)`](#fpohead).
1008 |
1009 | * **Arguments:**
1010 | - `v`: array, string, object
1011 |
1012 | * **Returns:** *any*
1013 |
1014 | * **Example:**
1015 |
1016 | ```js
1017 | var nums = [1,2,3,4];
1018 |
1019 | FPO.tail( {v: nums} );
1020 | // [2,3,4]
1021 |
1022 | FPO.tail( {v: []} );
1023 | // undefined
1024 |
1025 | FPO.tail( {v: "abc"} );
1026 | // "bc"
1027 |
1028 | FPO.tail( {v: {0: 42, 1: 10}} );
1029 | // {1: 10}
1030 | ```
1031 |
1032 | * **See Also:** [`FPO.head(..)`](#fpohead)
1033 |
1034 | ----
1035 |
1036 | ### `FPO.take(..)`
1037 |
1038 | ([back to top](#core-api))
1039 |
1040 | Returns the specified number of elements from the value, starting from the beginning.
1041 |
1042 | * **Arguments:**
1043 | - `v`: array / string
1044 | - `n`: number of elements to take from the beginning of the value; if omitted, defaults to `1`.
1045 |
1046 | * **Returns:** array / string
1047 |
1048 | * **Example:**
1049 |
1050 | ```js
1051 | var nums = [1,2,3,4];
1052 |
1053 | FPO.take( {v: nums, n: 2} );
1054 | // [1,2]
1055 |
1056 | FPO.take( {v: nums} );
1057 | // [1]
1058 |
1059 | FPO.take( {v: "abc", n: 2} );
1060 | // "ab"
1061 |
1062 | FPO.take( {v: null} );
1063 | // []
1064 | ```
1065 |
1066 | * **See Also:** [`FPO.head(..)`](#fpohead)
1067 |
1068 | ----
1069 |
1070 | ### `FPO.trampoline(..)`
1071 |
1072 | ([back to top](#core-api))
1073 |
1074 | Wraps a continuation-returning recursive function in another function that will run it until it no longer returns another continuation function. Trampolines are an alternative to tail calls.
1075 |
1076 | * **Arguments:**
1077 | - `fn`: function to run
1078 |
1079 | * **Returns:** *function*
1080 |
1081 | * **Example:**
1082 |
1083 | ```js
1084 | function sum(total,x) {
1085 | if (x <= 1) return total + x;
1086 | return () => sum( total + x, x - 1 );
1087 | }
1088 |
1089 | FPO.trampoline( {fn: sum} )( 0, 5 );
1090 | // 15
1091 | ```
1092 |
1093 | ----
1094 |
1095 | ### `FPO.transducers.array(..)`
1096 |
1097 | ([back to top](#core-api))
1098 |
1099 | A reducer function. For transducing purposes, a combination function that takes an array and a value, and mutates the array by pushing the value onto the end of it. The mutated array is returned.
1100 |
1101 | **This function has side-effects**, for performance reasons. It should be used with caution.
1102 |
1103 | * **Arguments:**
1104 | - `acc`: acculumator
1105 | - `v`: value
1106 |
1107 | * **Returns:** *array*
1108 |
1109 | * **Example:**
1110 |
1111 | ```js
1112 | var arr = [1,2,3];
1113 |
1114 | FPO.transducers.array( {acc: arr, v: 4} );
1115 | // [1,2,3,4]
1116 |
1117 | arr;
1118 | // [1,2,3,4] <-- was mutated as a side-effect!
1119 | ```
1120 |
1121 | * **See Also:** [`FPO.transducers.booleanAnd(..)`](#fpotransducersbooleanand),
1122 | * [`FPO.transducers.booleanOr(..)`](#fpotransducersbooleanor), [`FPO.transducers.default(..)`](#fpotransducersdefault), [`FPO.transducers.number(..)`](#fpotransducersnumber), [`FPO.transducers.string(..)`](#fpotransducersstring)
1123 |
1124 | ----
1125 |
1126 | ### `FPO.transducers.booleanAnd(..)`
1127 |
1128 | ([back to top](#core-api))
1129 |
1130 | A reducer function. For transducing purposes, a combination function that takes two booleans and *AND*s them together. The result is the logical *AND* of the two values.
1131 |
1132 | * **Arguments:**
1133 | - `acc`: acculumator
1134 | - `v`: value
1135 |
1136 | * **Returns:** *true/false*
1137 |
1138 | * **Example:**
1139 |
1140 | ```js
1141 | FPO.transducers.booleanAnd( {acc: true, v: true} );
1142 | // true
1143 |
1144 | FPO.transducers.booleanAnd( {acc: false, v: true} );
1145 | // false
1146 | ```
1147 |
1148 | * **Aliases:** `FPO.transducers.boolean(..)`
1149 |
1150 | * **See Also:** [`FPO.transducers.array(..)`](#fpotransducersarray),
1151 | * [`FPO.transducers.booleanOr(..)`](#fpotransducersbooleanor), [`FPO.transducers.default(..)`](#fpotransducersdefault), [`FPO.transducers.number(..)`](#fpotransducersnumber), [`FPO.transducers.string(..)`](#fpotransducersstring)
1152 |
1153 | ----
1154 |
1155 | ### `FPO.transducers.booleanOr(..)`
1156 |
1157 | ([back to top](#core-api))
1158 |
1159 | A reducer function. For transducing purposes, a combination function that takes two booleans and *OR*s them together. The result is the logical *OR* of the two values.
1160 |
1161 | * **Arguments:**
1162 | - `acc`: acculumator
1163 | - `v`: value
1164 |
1165 | * **Returns:** *true/false*
1166 |
1167 | * **Example:**
1168 |
1169 | ```js
1170 | FPO.transducers.booleanOr( {acc: false, v: true} );
1171 | // true
1172 |
1173 | FPO.transducers.booleanOr( {acc: false, v: false} );
1174 | // false
1175 | ```
1176 |
1177 | * **See Also:** [`FPO.transducers.array(..)`](#fpotransducersarray),
1178 | * [`FPO.transducers.booleanAnd(..)`](#fpotransducersbooleanand), [`FPO.transducers.default(..)`](#fpotransducersdefault), [`FPO.transducers.number(..)`](#fpotransducersnumber), [`FPO.transducers.string(..)`](#fpotransducersstring)
1179 |
1180 | ----
1181 |
1182 | ### `FPO.transducers.default(..)`
1183 |
1184 | ([back to top](#core-api))
1185 |
1186 | A reducer function. For transducing purposes, a combination function that's a default placeholder. It returns only the `acc` that's passed to it. The behavior here is almost the same as [`FPO.identity(..)`](#fpoidentity), except that returns `acc` instead of `v`.
1187 |
1188 | * **Arguments:**
1189 | - `acc`: acculumator
1190 | - `v`: value
1191 |
1192 | * **Returns:** *-any-*
1193 |
1194 | * **Example:**
1195 |
1196 | ```js
1197 | FPO.transducers.default( {acc: 3, v: 1} );
1198 | // 3
1199 | ```
1200 |
1201 | * **See Also:** [`FPO.transducers.array(..)`](#fpotransducersarray),
1202 | * [`FPO.transducers.booleanAnd(..)`](#fpotransducersbooleanand), [`FPO.transducers.booleanOr(..)`](#fpotransducersbooleanOr), [`FPO.transducers.number(..)`](#fpotransducersnumber), [`FPO.transducers.string(..)`](#fpotransducersstring), [`FPO.identity(..)`](#fpoidentity)
1203 |
1204 | ----
1205 |
1206 | ### `FPO.transducers.filter(..)`
1207 |
1208 | ([back to top](#core-api))
1209 |
1210 | For transducing purposes, wraps a predicate function as a filter-transducer. Typically, this filter-transducer is then composed with other filter-transducers and/or map-transducers. The resulting transducer is then passed to [`FPO.transducers.transduce(..)`](#fpotransducerstransduce).
1211 |
1212 | The filter-transducer is not a reducer itself; it's expecting a combination function (reducer), which will then produce a filter-reducer. So alternately, you can manually create the filter-reducer and use it directly with a regular [`FPO.reduce(..)`](#fporeduce) reduction.
1213 |
1214 | * **Arguments:**
1215 | - `fn`: predicate function
1216 |
1217 | * **Returns:** *function*
1218 |
1219 | * **Example:**
1220 |
1221 | ```js
1222 | function isOdd({ v }) { return v % 2 == 1; }
1223 | function arrayPush({ acc, v }) { acc.push( v ); return acc; }
1224 |
1225 | var nums = [1,2,3,4,5];
1226 |
1227 | var filterTransducer = FPO.transducers.filter( {fn: isOdd} );
1228 |
1229 | FPO.transducers.transduce(
1230 | {fn: filterTransducer, co: arrayPush, v: [], arr: nums}
1231 | );
1232 | // [1,3,5]
1233 |
1234 | // ******************
1235 |
1236 | var filterReducer = filterTransducer( {v: arrayPush} );
1237 |
1238 | filterReducer( {acc: [], v: 3} );
1239 | // [3]
1240 |
1241 | filterReducer( {acc: [], v: 4} );
1242 | // []
1243 |
1244 | FPO.reduce( {fn: filterReducer, v: [], arr: nums} );
1245 | // [1,3,5]
1246 | ```
1247 |
1248 | * **See Also:** [`FPO.transducers.map(..)`](#fpotransducersmap)
1249 |
1250 | ----
1251 |
1252 | ### `FPO.transducers.into(..)`
1253 |
1254 | ([back to top](#core-api))
1255 |
1256 | Selects an appropriate combination function (reducer) based on the provided initial value. Then runs [`FPO.transducers.transduce(..)`](#fpotransducerstransduce) under the covers.
1257 |
1258 | Detects initial values of `boolean`, `number`, `string`, and `array` types, and dispatches to the appropriate combination function accordingly ([`FPO.transducers.number(..)`](#fpotransducersnumber), etc). **Note:** A `boolean` initial value selects [`FPO.transducers.booleanAnd(..)`](#fpotransducersboooleanand).
1259 |
1260 | **Note:** When composing transducers, the effective order of operations is reversed from normal composition. Instead of expecting composition to be right-to-left, the effective order will be left-to-right (see below).
1261 |
1262 | * **Arguments:**
1263 | - `fn`: transducer function
1264 | - `v`: initial value for the reduction; also used to select the appropriate combination function (reducer) for the transducing.
1265 | - `arr`: the list for the reduction
1266 |
1267 | * **Returns:** *-any-*
1268 |
1269 | * **Example:**
1270 |
1271 | ```js
1272 | function double({ v }) { return v * 2; }
1273 | function isOdd({ v }) { return v % 2 == 1; }
1274 |
1275 | var nums = [1,2,3,4,5];
1276 |
1277 | var transducer = FPO.compose( {fns: [
1278 | FPO.transducers.filter( {fn: isOdd} ),
1279 | FPO.transducers.map( {fn: double} )
1280 | ]} );
1281 |
1282 | FPO.transducers.into( {fn: transducer, v: [], arr: nums} );
1283 | // [2,6,10]
1284 |
1285 | FPO.transducers.into( {fn: transducer, v: 0, arr: nums} );
1286 | // 18
1287 |
1288 | FPO.transducers.into( {fn: transducer, v: "", arr: nums} );
1289 | // "2610"
1290 | ```
1291 |
1292 | * **See Also:** [`FPO.transducers.transduce(..)`](#fpotransducerstransduce)
1293 |
1294 | ----
1295 |
1296 | ### `FPO.transducers.map(..)`
1297 |
1298 | ([back to top](#core-api))
1299 |
1300 | For transducing purposes, wraps a mapper function as a map-transducer. Typically, this map-transducer is then composed with other filter-transducers and/or map-transducers. The resulting transducer is then passed to [`FPO.transducers.transduce(..)`](#fpotransducerstransduce).
1301 |
1302 | The map-transducer is not a reducer itself; it's expecting a combination function (reducer), which will then produce a filter-reducer. So alternately, you can manually create the map-reducer and use it directly with a regular [`FPO.reduce(..)`](#fporeduce) reduction.
1303 |
1304 | * **Arguments:**
1305 | - `fn`: mapper function
1306 |
1307 | * **Returns:** *function*
1308 |
1309 | * **Example:**
1310 |
1311 | ```js
1312 | function double({ v }) { return v * 2; }
1313 | function arrayPush({ acc, v }) { acc.push( v ); return acc; }
1314 |
1315 | var nums = [1,2,3,4,5];
1316 |
1317 | var mapTransducer = FPO.transducers.map( {fn: double} );
1318 |
1319 | FPO.transducers.transduce(
1320 | {fn: mapTransducer, co: arrayPush, v: [], arr: nums}
1321 | );
1322 | // [2,4,6,8,10]
1323 |
1324 | // ******************
1325 |
1326 | var mapReducer = mapTransducer( {v: arrayPush} );
1327 |
1328 | mapReducer( {acc: [], v: 3} );
1329 | // [6]
1330 |
1331 | FPO.reduce( {fn: mapReducer, v: [], arr: nums} );
1332 | // [2,4,6,8,10]
1333 | ```
1334 |
1335 | * **See Also:** [`FPO.transducers.filter(..)`](#fpotransducersfilter)
1336 |
1337 | ----
1338 |
1339 | ### `FPO.transducers.number(..)`
1340 |
1341 | ([back to top](#core-api))
1342 |
1343 | A reducer function. For transducing purposes, a combination function that adds together the two numbers passed into it. The result is the sum.
1344 |
1345 | * **Arguments:**
1346 | - `acc`: acculumator
1347 | - `v`: value
1348 |
1349 | * **Returns:** *number*
1350 |
1351 | * **Example:**
1352 |
1353 | ```js
1354 | FPO.transducers.number( {acc: 3, v: 4} );
1355 | // 7
1356 | ```
1357 |
1358 | * **See Also:** [`FPO.transducers.array(..)`](#fpotransducersarray),
1359 | * [`FPO.transducers.booleanAnd(..)`](#fpotransducersbooleanand), [`FPO.transducers.booleanOr(..)`](#fpotransducersbooleanOr), [`FPO.transducers.default(..)`](#fpotransducersdefault), [`FPO.transducers.string(..)`](#fpotransducersstring)
1360 |
1361 | ----
1362 |
1363 | ### `FPO.transducers.string(..)`
1364 |
1365 | ([back to top](#core-api))
1366 |
1367 | A reducer function. For transducing purposes, a combination function that concats the two strings passed into it. The result is the concatenation.
1368 |
1369 | * **Arguments:**
1370 | - `acc`: acculumator
1371 | - `v`: value
1372 |
1373 | * **Returns:** *string*
1374 |
1375 | * **Example:**
1376 |
1377 | ```js
1378 | FPO.transducers.string( {acc: "hello", v: "world"} );
1379 | // "helloworld"
1380 | ```
1381 |
1382 | * **See Also:** [`FPO.transducers.array(..)`](#fpotransducersarray),
1383 | * [`FPO.transducers.booleanAnd(..)`](#fpotransducersbooleanand), [`FPO.transducers.booleanOr(..)`](#fpotransducersbooleanOr), [`FPO.transducers.default(..)`](#fpotransducersdefault), [`FPO.transducers.number(..)`](#fpotransducersnumber)
1384 |
1385 | ----
1386 |
1387 | ### `FPO.transducers.transduce(..)`
1388 |
1389 | ([back to top](#core-api))
1390 |
1391 | Produces a reducer from a specified transducer and combination function. Then runs a reduction on a list, using that reducer, starting with the specified initial value.
1392 |
1393 | **Note:** When composing transducers, the effective order of operations is reversed from normal composition. Instead of expecting composition to be right-to-left, the effective order will be left-to-right (see below).
1394 |
1395 | * **Arguments:**
1396 | - `fn`: transducer function
1397 | - `co`: combination function for the transducer
1398 | - `v`: initial value for the reduction
1399 | - `arr`: the list for the reduction
1400 |
1401 | * **Returns:** *-any-*
1402 |
1403 | * **Example:**
1404 |
1405 | ```js
1406 | function double({ v }) { return v * 2; }
1407 | function isOdd({ v }) { return v % 2 == 1; }
1408 | function arrayPush({ acc, v }) { acc.push( v ); return acc; }
1409 |
1410 | var nums = [1,2,3,4,5];
1411 |
1412 | var transducer = FPO.compose( {fns: [
1413 | FPO.transducers.filter( {fn: isOdd} ),
1414 | FPO.transducers.map( {fn: double} )
1415 | ]} );
1416 |
1417 | FPO.transducers.transduce(
1418 | {fn: transducer, co: arrayPush, v: [], arr: nums}
1419 | );
1420 | // [2,6,10]
1421 | ```
1422 |
1423 | * **See Also:** [`FPO.transducers.into(..)`](#fpotransducersinto)
1424 |
1425 | ----
1426 |
1427 | ### `FPO.unapply(..)`
1428 |
1429 | ([back to top](#core-api))
1430 |
1431 | Wraps a function to gather individual positional arguments into an object argument.
1432 |
1433 | * **Arguments:**
1434 | - `fn`: function to wrap
1435 | - `props`: list of property names (strings) to indicate the order to gather individual positional arguments as properties.
1436 |
1437 | * **Returns:** *function*
1438 |
1439 | * **Example:**
1440 |
1441 | ```js
1442 | function foo({ x, y }) { return x + y; }
1443 |
1444 | var f = FPO.unapply( {fn: foo, props:["x","y"]} );
1445 |
1446 | f( 1, 2 ); // 3
1447 | ```
1448 |
1449 | * **Aliases:** `FPO.gather(..)`
1450 |
1451 | * **See Also:** [`FPO.apply(..)`](#fpoapply)
1452 |
1453 | ----
1454 |
1455 | ### `FPO.unary(..)`
1456 |
1457 | ([back to top](#core-api))
1458 |
1459 | Wraps a function to restrict its inputs to only one named argument as specified.
1460 |
1461 | * **Arguments:**
1462 | - `fn`: function to wrap
1463 | - `prop`: property name to allow as named argument
1464 |
1465 | * **Returns:** *function*
1466 |
1467 | * **Example:**
1468 |
1469 | ```js
1470 | functino foo(argsObj) { return argsObj; }
1471 |
1472 | var f = FPO.unary( {fn: foo, prop: "y"} );
1473 |
1474 | f( {x: 1, y: 2, z: 3} );
1475 | // { y:2 }
1476 | ```
1477 |
1478 | * **See Also:** [`FPO.nAry(..)`](#fponary), [`FPO.binary(..)`](#fpobinary)
1479 |
1480 | ----
1481 |
1482 | ### `FPO.uncurry(..)`
1483 |
1484 | ([back to top](#core-api))
1485 |
1486 | Wraps a (strictly) curried function in a new function that accepts all the arguments at once, and provides them one at a time to the underlying curried function.
1487 |
1488 | * **Arguments:**
1489 | - `fn`: function to uncurry
1490 |
1491 | * **Returns:** *function*
1492 |
1493 | * **Example:**
1494 |
1495 | ```js
1496 | function foo({ x,y,z }) { return x + y + z; }
1497 |
1498 | var f = FPO.curry( {fn: foo, n: 3} );
1499 |
1500 | var p = FPO.uncurry( {fn: f} );
1501 |
1502 | p( {x: 1, y: 2, z: 3} ); // 6
1503 | ```
1504 |
1505 | * **See Also:** [`FPO.curryMultiple(..)`](#fpocurrymultiple), [`FPO.partial(..)`](#fpopartial), [`FPO.uncurry(..)`](#fpouncurry)
1506 |
1507 | ----
1508 |
1509 | ### `FPO.zip(..)`
1510 |
1511 | ([back to top](#core-api))
1512 |
1513 | Produces a new array where each element is an array containing the value from that position in each of the respective input arrays. Only processes up to the shorter of the two arrays.
1514 |
1515 | * **Arguments:**
1516 | - `arr1`: the first array
1517 | - `arr2`: the second array
1518 |
1519 | * **Returns:** *array*
1520 |
1521 | * **Example:**
1522 |
1523 | ```js
1524 | var nums1 = [1,3,5,7];
1525 | var nums2 = [2,4,6];
1526 |
1527 | FPO.zip( {arr1: nums1, arr2: nums2} );
1528 | // [[1,2],[3,4],[5,6]]
1529 | ```
1530 |
1531 | * **See Also:** [`FPO.flatten(..)`](#fpoflatten)
1532 |
--------------------------------------------------------------------------------
/docs/std-API.md:
--------------------------------------------------------------------------------
1 | # Standard API
2 |
3 | These are the methods on the `FPO.std.*` namespace. For the `FPO.*` methods, consult the [Core API documentation](core-API.md).
4 |
5 | * [`FPO.std.apply(..)`](#fpostdapply) (aliases: `FPO.std.spread(..)`)
6 | * [`FPO.std.binary(..)`](#fpostdbinary)
7 | * [`FPO.std.complement(..)`](#fpostdcomplement)
8 | * [`FPO.std.compose(..)`](#fpostdcompose) (aliases: `FPO.std.flowRight(..)`)
9 | * [`FPO.std.constant(..)`](#fpostdconstant)
10 | * [`FPO.std.curry(..)`](#fpostdcurry)
11 | * [`FPO.std.curryMultiple(..)`](#fpostdcurrymultiple)
12 | * [`FPO.std.filterIn(..)`](#fpostdfilterin) (aliases: `FPO.std.filter(..)`, `FPO.std.keep(..)`)
13 | * [`FPO.std.filterInObj(..)`](#fpostdfilterinobj) (aliases: `FPO.std.filterObj(..)`, `FPO.std.keepObj(..)`)
14 | * [`FPO.std.filterOut(..)`](#fpostdfilterout) (aliases: `FPO.std.reject(..)`)
15 | * [`FPO.std.filterOutObj(..)`](#fpostdfilteroutobj) (aliases: `FPO.std.rejectObj(..)`)
16 | * [`FPO.std.flatMap(..)`](#fpostdflatmap) (aliases: `FPO.std.chain(..)`)
17 | * [`FPO.std.flatMapObj(..)`](#fpostdflatmapobj) (aliases: `FPO.std.chainObj(..)`)
18 | * [`FPO.std.flatten(..)`](#fpostdflatten)
19 | * [`FPO.std.flip(..)`](#fpostdflip)
20 | * [`FPO.std.head(..)`](#fpostdhead)
21 | * [`FPO.std.identity(..)`](#fpostdidentity)
22 | * [`FPO.std.map(..)`](#fpostdmap)
23 | * [`FPO.std.mapObj(..)`](#fpostdmapobj)
24 | * [`FPO.std.memoize(..)`](#fpostdmemoize)
25 | * [`FPO.std.nAry(..)`](#fpostdnary)
26 | * [`FPO.std.partial(..)`](#fpostdpartial)
27 | * [`FPO.std.partialRight(..)`](#fpostdpartialright)
28 | * [`FPO.std.pick(..)`](#fpostdpick)
29 | * [`FPO.std.pickAll(..)`](#fpostdpickall)
30 | * [`FPO.std.pipe(..)`](#fpostdpipe) (aliases: `FPO.std.flow(..)`, `FPO.std.sequence(..)`)
31 | * [`FPO.std.prop(..)`](#fpostdprop)
32 | * [`FPO.std.reassoc(..)`](#fpostdreassoc)
33 | * [`FPO.std.reduce(..)`](#fpostdreduce) (aliases: `FPO.std.fold(..)`, `FPO.std.foldL(..)`)
34 | * [`FPO.std.reduceObj(..)`](#fpostdreduceobj) (aliases: `FPO.std.foldObj(..)`)
35 | * [`FPO.std.reduceRight(..)`](#fpostdreduceright) (aliases: `FPO.std.foldR(..)`)
36 | * [`FPO.std.remap(..)`](#fpostdremap)
37 | * [`FPO.std.reverseArgs(..)`](#fpostdreverseargs)
38 | * [`FPO.std.setProp(..)`](#fpostdsetprop) (aliases: `FPO.std.assoc(..)`)
39 | * [`FPO.std.tail(..)`](#fpostdtail)
40 | * [`FPO.std.take(..)`](#fpostdtake)
41 | * [`FPO.std.trampoline(..)`](#fpostdtrampoline)
42 | * **Transducers**:
43 | - [`FPO.std.transducers.array(..)`](#fpostdtransducersarray)
44 | - [`FPO.std.transducers.booleanAnd(..)`](#fpostdtransducersbooleanand) (aliases: `FPO.std.transducers.boolean(..)`)
45 | - [`FPO.std.transducers.booleanOr(..)`](#fpostdtransducersbooleanor)
46 | - [`FPO.std.transducers.default(..)`](#fpostdtransducersdefault)
47 | - [`FPO.std.transducers.filter(..)`](#fpostdtransducersfilter)
48 | - [`FPO.std.transducers.into(..)`](#fpostdtransducersinto)
49 | - [`FPO.std.transducers.map(..)`](#fpostdtransducersmap)
50 | - [`FPO.std.transducers.number(..)`](#fpostdtransducersnumber)
51 | - [`FPO.std.transducers.string(..)`](#fpostdtransducersstring)
52 | - [`FPO.std.transducers.transduce(..)`](#fpostdtransducerstransduce)
53 | * [`FPO.std.unapply(..)`](#fpostdunapply) (aliases: `FPO.std.gather(..)`)
54 | * [`FPO.std.unary(..)`](#fpostdunary)
55 | * [`FPO.std.uncurry(..)`](#fpostduncurry)
56 | * [`FPO.std.zip(..)`](#fpostdzip)
57 |
58 | ----
59 |
60 | ### `FPO.std.apply(..)`
61 |
62 | ([back to top](#standard-api))
63 |
64 | Wraps a function to spread out the elements from an array argument as individual positional arguments.
65 |
66 | * **Arguments:**
67 | - `fn`: function to wrap
68 |
69 | * **Returns:** *function*
70 |
71 | * **Example:**
72 |
73 | ```js
74 | function foo(x,y = 2) { return x + y; }
75 | function bar([a,b],c) { return a + b + c; }
76 |
77 | var f = FPO.std.apply( foo );
78 | var p = FPO.std.apply( bar );
79 |
80 | f( [1,1] ); // 2
81 | f( [3] ); // 5
82 | p( [[1,2],3] ); // 6
83 | ```
84 |
85 | * **Aliases:** `FPO.std.spread(..)`
86 |
87 | * **See Also:** [`FPO.std.unapply(..)`](#fpostdunapply)
88 |
89 | ----
90 |
91 | ### `FPO.std.binary(..)`
92 |
93 | ([back to top](#standard-api))
94 |
95 | Wraps a function to restrict its inputs to only arguments.
96 |
97 | * **Arguments:**
98 | - `fn`: function to wrap
99 |
100 | * **Returns:** *function*
101 |
102 | * **Example:**
103 |
104 | ```js
105 | function foo(...args) { return args; }
106 |
107 | var f = FPO.std.binary( foo );
108 |
109 | f( 1, 2, 3 ); // [1,2]
110 | ```
111 |
112 | * **See Also:** [`FPO.std.nAry(..)`](#fpostdnary), [`FPO.std.unary(..)`](#fpostdunary)
113 |
114 | ----
115 |
116 | ### `FPO.std.complement(..)`
117 |
118 | ([back to top](#standard-api))
119 |
120 | Wraps a predicate function -- a function that produces `true` / `false` -- to negate its result.
121 |
122 | * **Arguments:**
123 | - `fn`: function to wrap
124 |
125 | * **Returns:** *function*
126 |
127 | * **Example:**
128 |
129 | ```js
130 | var f = FPO.std.complement( () => false );
131 |
132 | f(); // true
133 | ```
134 |
135 | ----
136 |
137 | ### `FPO.std.compose(..)`
138 |
139 | ([back to top](#standard-api))
140 |
141 | Produces a new function that's the composition of a list of functions. Functions are composed right-to-left (unlike [`FPO.std.pipe(..)`](#fpostdpipe)) from the array.
142 |
143 | * **Arguments:**
144 | - `fns`: array of functions
145 |
146 | * **Returns:** *function*
147 |
148 | * **Example:**
149 |
150 | ```js
151 | var f = FPO.std.compose( [
152 | v => v + "3",
153 | v => v + "2",
154 | v => v + "1"
155 | ] );
156 |
157 | f( "0" ); // "0123"
158 | ```
159 |
160 | * **Aliases:** `FPO.std.flowRight(..)`
161 |
162 | * **See Also:** [`FPO.std.pipe(..)`](#fpostdpipe)
163 |
164 | ----
165 |
166 | ### `FPO.std.constant(..)`
167 |
168 | ([back to top](#standard-api))
169 |
170 | Wraps a value in a function that returns the value.
171 |
172 | * **Arguments:**
173 | - `v`: value to wrap
174 |
175 | * **Returns:** *function*
176 |
177 | * **Example:**
178 |
179 | ```js
180 | var f = FPO.std.constant( 42 );
181 |
182 | f(); // 42
183 | ```
184 |
185 | * **See Also:** [`FPO.std.identity(..)`](#fpostdidentity)
186 |
187 | ----
188 |
189 | ### `FPO.std.curry(..)`
190 |
191 | ([back to top](#standard-api))
192 |
193 | Curries a function so that you can pass one argument at a time, each time getting back another function to receive the next argument. Once all arguments are passed, the underlying function is called with the arguments.
194 |
195 | * **Arguments:**
196 | - `fn`: function to curry
197 | - `n`: (optional) number of arguments to curry for; if omitted, tries to detect the arity (`fn.length`) to use.
198 |
199 | * **Returns:** *function*
200 |
201 | * **Example:**
202 |
203 | ```js
204 | function foo(x,y,z) { return x + y + z; }
205 |
206 | var f = FPO.std.curry( foo, 3 );
207 | var p = FPO.std.curry( foo );
208 |
209 | f()( "a" )( "b" )()( "c" );
210 | // "abc"
211 |
212 | p( "a" )( "b" )( "c" );
213 | // "abc"
214 | ```
215 |
216 | * **See Also:** [`FPO.std.curryMultiple(..)`](#fpostdcurrymultiple), [`FPO.std.partial(..)`](#fpostdpartial), [`FPO.std.uncurry(..)`](#fpostduncurry)
217 |
218 | ----
219 |
220 | ### `FPO.std.curryMultiple(..)`
221 |
222 | ([back to top](#standard-api))
223 |
224 | Just like [`FPO.std.curry(..)`](#fpostdcurry), except each curried function allows multiple arguments instead of just one.
225 |
226 | * **Arguments:**
227 | - `fn`: function to curry
228 | - `n`: (optional) number of arguments to curry for; if omitted, tries to detect the arity (`fn.length`) to use.
229 |
230 | * **Returns:** *function*
231 |
232 | * **Example:**
233 |
234 | ```js
235 | function foo(x,y,z) { return x + y + z; }
236 |
237 | var f = FPO.std.curryMultiple( foo, 3 );
238 | var p = FPO.std.curryMultiple( foo );
239 |
240 | f()( "a" )()( "b", "c" );
241 | // "abc"
242 |
243 | p( "a" )( "b", "c" );
244 | // "abc"
245 | ```
246 |
247 | * **See Also:** [`FPO.std.curry(..)`](#fpostdcurry), [`FPO.std.partial(..)`](#fpostdpartial), [`FPO.std.uncurry(..)`](#fpostduncurry)
248 |
249 | ----
250 |
251 | ### `FPO.std.filterIn(..)`
252 |
253 | ([back to top](#standard-api))
254 |
255 | Commonly known as `filter(..)`, produces a new list by calling a predicate function with each value in the original list. For each value, if the predicate function returns true (or truthy), the value is included in (aka, filtered into) the new list. Otherwise, the value is omitted.
256 |
257 | * **Arguments:**
258 | - `fn`: predicate function; called with `v` (value), `i` (index), and `arr` (array) arguments
259 | - `arr`: array to filter against
260 |
261 | * **Returns:** *array*
262 |
263 | * **Example:**
264 |
265 | ```js
266 | function isOdd(v) { return v % 2 == 1; }
267 |
268 | var nums = [1,2,3,4,5];
269 |
270 | FPO.std.filterIn( isOdd, nums );
271 | // [1,3,5]
272 | ```
273 |
274 | * **Aliases:** `FPO.std.filter(..)`, `FPO.std.keep(..)`
275 |
276 | * **See Also:** [`FPO.std.filterInObj(..)`](#fpostdfilterinobj), [`FPO.std.filterOut(..)`](#fpostdfilterout)
277 |
278 | ----
279 |
280 | ### `FPO.std.filterInObj(..)`
281 |
282 | ([back to top](#standard-api))
283 |
284 | Produces a new object by calling a predicate function with each property value in the original object. For each value, if the predicate function returns true (or truthy), the value is included in (aka, filtered into) the new object at the same property name. Otherwise, the value is omitted.
285 |
286 | * **Arguments:**
287 | - `fn`: predicate function; called with `v` (value), `i` (property name), and `o` (object) arguments
288 | - `o`: object to filter against
289 |
290 | * **Returns:** *object*
291 |
292 | * **Example:**
293 |
294 | ```js
295 | function isOdd(v) { return v % 2 == 1; }
296 |
297 | var nums = {a: 1, b: 2, c: 3, d: 4, e: 5};
298 |
299 | FPO.std.filterInObj( isOdd, nums );
300 | // {a: 1, c: 3, e: 5}
301 | ```
302 |
303 | * **Aliases:** `FPO.std.filterObj(..)`, `FPO.std.keepObj(..)`
304 |
305 | * **See Also:** [`FPO.std.filterIn(..)`](#fpostdfilterin), [`FPO.std.filterOutObj(..)`](#fpostdfilteroutobj)
306 |
307 | ----
308 |
309 | ### `FPO.std.filterOut(..)`
310 |
311 | ([back to top](#standard-api))
312 |
313 | The inverse of [`FPO.std.filterIn(..)`](#fpostdfilterin), produces a new list by calling a predicate function with each value in the original list. For each value, if the predicate function returns true (or truthy), the value is omitted from (aka, filtered out of) the new list. Otherwise, the value is included.
314 |
315 | * **Arguments:**
316 | - `fn`: predicate function; called with `v` (value), `i` (index), and `arr` (array) arguments
317 | - `arr`: array to filter against
318 |
319 | * **Returns:** *array*
320 |
321 | * **Example:**
322 |
323 | ```js
324 | function isOdd(v) { return v % 2 == 1; }
325 |
326 | var nums = [1,2,3,4,5];
327 |
328 | FPO.std.filterOut( isOdd, nums );
329 | // [2,4]
330 | ```
331 |
332 | * **Aliases:** `FPO.std.reject(..)`
333 |
334 | * **See Also:** [`FPO.std.filterOutObj(..)`](#fpostdfilteroutobj), [`FPO.std.filterIn(..)`](#fpostdfilterin)
335 |
336 | ----
337 |
338 | ### `FPO.std.filterOutObj(..)`
339 |
340 | ([back to top](#standard-api))
341 |
342 | The inverse of [`FPO.std.filterInObj(..)`](#fpostdfilterinobj), produces a new object by calling a predicate function with each property value in the original object. For each value, if the predicate function returns true (or truthy), the value is omitted from (aka, filtered out of) the new object. Otherwise, the value is included at the same property name.
343 |
344 | * **Arguments:**
345 | - `fn`: predicate function; called with `v` (value), `i` (property name), and `o` (object) arguments
346 | - `o`: object to filter against
347 |
348 | * **Returns:** *object*
349 |
350 | * **Example:**
351 |
352 | ```js
353 | function isOdd(v) { return v % 2 == 1; }
354 |
355 | var nums = {a: 1, b: 2, c: 3, d: 4, e: 5};
356 |
357 | FPO.std.filterOutObj( isOdd, nums );
358 | // {b: 2, d: 4}
359 | ```
360 |
361 | * **Aliases:** `FPO.std.rejectObj(..)`
362 |
363 | * **See Also:** [`FPO.std.filterOut(..)`](#fpostdfilterout), [`FPO.std.filterInObj(..)`](#fpostdfilterinobj)
364 |
365 | ----
366 |
367 | ### `FPO.std.flatMap(..)`
368 |
369 | ([back to top](#standard-api))
370 |
371 | Similar to [`FPO.std.map(..)`](#fpostdmap), produces a new list by calling a mapper function with each value in the original list. If the mapper function returns an array, this array is flattened (one level) into the overall array.
372 |
373 | * **Arguments:**
374 | - `fn`: mapper function; called with `v` (value), `i` (index), and `arr` (array) arguments
375 | - `arr`: array to flat-map against
376 |
377 | * **Returns:** *array*
378 |
379 | * **Example:**
380 |
381 | ```js
382 | function splitChars(v) { return [...v]; }
383 |
384 | var words = ["hello","world"];
385 |
386 | splitChars( words[0] );
387 | // ["h","e","l","l","o"]
388 |
389 | FPO.std.map( splitChars, words );
390 | // [["h","e","l","l","o"],["w","o","r","l","d"]]
391 |
392 | FPO.std.flatMap( splitChars, words );
393 | // ["h","e","l","l","o","w","o","r","l","d"]
394 | ```
395 |
396 | * **Aliases:** `FPO.std.chain(..)`
397 |
398 | * **See Also:** [`FPO.std.flatMapObj(..)`](#fpostdflatmapobj), [`FPO.std.map(..)`](#fpostdmap), [`FPO.std.flatten(..)`](#fpostdflatten)
399 |
400 | ----
401 |
402 | ### `FPO.std.flatMapObj(..)`
403 |
404 | ([back to top](#standard-api))
405 |
406 | Similar to [`FPO.std.mapObj(..)`](#fpostdmapobj), produces a new object by calling a mapper function with each property value in the original object. If the mapper function returns an object, this object is flattened (one level) into the overall object, by copying its properties.
407 |
408 | * **Arguments:**
409 | - `fn`: mapper function; called with `v` (value), `i` (property name), and `o` (object) arguments
410 | - `o`: object to flat-map against
411 |
412 | * **Returns:** *object*
413 |
414 | * **Example:**
415 |
416 | ```js
417 | function splitEvensInHalf(v,i) {
418 | if (v % 2 == 0) {
419 | return { [i]: v/2, [i+"_2"]: v/2 };
420 | }
421 | return v;
422 | }
423 |
424 | var nums = {a: 1, b: 2, c: 3, d: 4};
425 |
426 | splitEvensInHalf( 3, "c" );
427 | // 3
428 |
429 | splitEvensInHalf( 4, "d" );
430 | // {d: 2, d_2: 2}
431 |
432 | FPO.std.mapObj( splitEvensInHalf, nums );
433 | // {a: 1, b: {b: 1, b_2: 1}, c: 3, d: {d: 2, d_2: 2}}
434 |
435 | FPO.std.flatMapObj( splitEvensInHalf, nums );
436 | // {a: 1, b: 1, b_2: 1, c: 3, d: 2, d_2: 2};
437 | ```
438 |
439 | * **Aliases:** `FPO.std.chainObj(..)`
440 |
441 | * **See Also:** [`FPO.std.flatMap(..)`](#fpostdflatmap), [`FPO.std.mapObj(..)`](#fpostdmapobj)
442 |
443 | ----
444 |
445 | ### `FPO.std.flatten(..)`
446 |
447 | ([back to top](#standard-api))
448 |
449 | Flattens an array of nested arrays. Optionally, specify how many levels of nesting to flatten out.
450 |
451 | * **Arguments:**
452 | - `arr`: array to flat-map against
453 | - `n`: (optional) the number of levels of nesting to flatten out; if omitted, defaults to Infinity (to flatten any nested depth).
454 |
455 | * **Returns:** *array*
456 |
457 | * **Example:**
458 |
459 | ```js
460 | var nums = [1,2,[3,4],[5,[6,7]]];
461 |
462 | FPO.std.flatten( nums );
463 | // [1,2,3,4,5,6,7]
464 |
465 | FPO.std.flatten( nums, 1 );
466 | // [1,2,3,4,5,[6,7]]
467 |
468 | FPO.std.flatten( nums, 2 );
469 | // [1,2,3,4,5,6,7]
470 | ```
471 |
472 | * **See Also:** [`FPO.std.flatMap(..)`](#fpostdflatmap)
473 |
474 | ----
475 |
476 | ### `FPO.std.flip(..)`
477 |
478 | ([back to top](#standard-api))
479 |
480 | Wraps a function to flip (transpose) the first two arguments it receives.
481 |
482 | * **Arguments:**
483 | - `fn`: function to wrap
484 |
485 | * **Returns:** *function*
486 |
487 | * **Example:**
488 |
489 | ```js
490 | function foo(...args) { return args; }
491 |
492 | var f = FPO.std.flip( foo );
493 |
494 | f( 1, 2, 3, 4 );
495 | // [2,1,3,4]
496 | ```
497 |
498 | * **See Also:** [`FPO.std.reverseArgs(..)`](#fpostdreverseargs)
499 |
500 | ----
501 |
502 | ### `FPO.std.head(..)`
503 |
504 | ([back to top](#standard-api))
505 |
506 | Returns the element as accessed at index 0 of the value.
507 |
508 | * **Arguments:**
509 | - `v`: array, string, object
510 |
511 | * **Returns:** *any*
512 |
513 | * **Example:**
514 |
515 | ```js
516 | var nums = [1,2,3,4];
517 |
518 | FPO.std.head( nums );
519 | // 1
520 |
521 | FPO.std.head( [] );
522 | // undefined
523 |
524 | FPO.std.head( "abc" );
525 | // "a"
526 |
527 | FPO.std.head( {0: 42} );
528 | // 42
529 | ```
530 |
531 | * **See Also:** [`FPO.tail(..)`](#fpotail)
532 |
533 | ----
534 |
535 | ### `FPO.std.identity(..)`
536 |
537 | ([back to top](#standard-api))
538 |
539 | Returns the value given to it. Useful as a default placeholder for certain opertaions (i.e., composition, reduction).
540 |
541 | * **Arguments:**
542 | - `v`: value to return
543 |
544 | * **Returns:** *-any-*
545 |
546 | * **Example:**
547 |
548 | ```js
549 | FPO.std.identity( 42 ); // 42
550 | ```
551 |
552 | * **See Also:** [`FPO.std.constant(..)`](#fpostdconstant)
553 |
554 | ----
555 |
556 | ### `FPO.std.map(..)`
557 |
558 | ([back to top](#standard-api))
559 |
560 | Produces a new list by calling a mapper function with each value in the original list. The value the mapper function returns is inserted in the new list at that same position. The new list will always be the same length as the original list.
561 |
562 | * **Arguments:**
563 | - `fn`: mapper function; called with `v` (value), `i` (index), and `arr` (array) arguments
564 | - `arr`: array to map against
565 |
566 | * **Returns:** *array*
567 |
568 | * **Example:**
569 |
570 | ```js
571 | function double(v) { return v * 2; }
572 |
573 | var nums = [1,2,3,4,5];
574 |
575 | FPO.std.map( double, nums );
576 | // [2,4,6,8,10]
577 | ```
578 |
579 | * **See Also:** [`FPO.std.mapObj(..)`](#fpostdmapobj), [`FPO.std.flatMap(..)`](#fpostdflatmap)
580 |
581 | ----
582 |
583 | ### `FPO.std.mapObj(..)`
584 |
585 | ([back to top](#standard-api))
586 |
587 | Produces a new object by calling a mapper function with each property value in the original object. The value the mapper function returns is inserted in the new object at that same property name. The new object will always have the same number of properties as the original object.
588 |
589 | * **Arguments:**
590 | - `fn`: mapper function; called with `v` (value), `i` (property name), and `o` (object) arguments
591 | - `o`: object to map against
592 |
593 | * **Returns:** *object*
594 |
595 | * **Example:**
596 |
597 | ```js
598 | function double(v) { return v * 2; }
599 |
600 | var nums = {a: 1, b: 2, c: 3, d: 4, e: 5};
601 |
602 | FPO.mapObj(double, nums );
603 | // {a: 2, b: 4, c: 6, d: 8, e: 10};
604 | ```
605 |
606 | * **See Also:** [`FPO.std.map(..)`](#fpostdmap)
607 |
608 | ----
609 |
610 | ### `FPO.std.memoize(..)`
611 |
612 | ([back to top](#standard-api))
613 |
614 | For performance optimization reasons, wraps a function such that it remembers each set of arguments passed to it, associated with that underlying return value. If the wrapped function is called subsequent times with the same set of arguments, the cached return value is returned instead of being recomputed. Each wrapped function instance has its own separate cache, even if wrapping the same original function multiple times.
615 |
616 | A set of arguments is "remembered" by being hashed to a string value to use as a cache key. This hashing is done internally with `JSON.stringify(..)`, which is fast and works with many common JS value types. However, this hashing is by no means bullet-proof for all types, and does not guarantee collision-free. **Use caution:** generally, you should only use primitives (number, string, boolean, null, and undefined) or simple objects (object, array) as arguments. If you use objects, always make sure to list properties in the same order to ensure proper hashing.
617 |
618 | By default, the function's arity (`fn.length`) will be detected as `n`. However, in JS certain techniques thwart this detection, such as the use of default parameters or parameter destructuring. Make sure to specify the correct `n` if detection is uncertain or unreliable.
619 |
620 | Unary functions (single argument; `n` of `1`) with a primitive argument are the fastest for memoization, so if possible, try to design functions that way. In these cases, specifying `n` as `1` will help ensure the best possible performance.
621 |
622 | **Warning:** Be aware that if `1` is initially specified (or detected) for `n`, additional arguments later passed to the wrapped function are **not considered** in the memoization hashing, though they will still be passed to the underlying function as-is. This may cause unexpected results (false-positives on cache hits); always make sure `n` matches the expected number of arguments.
623 |
624 | * **Arguments:**
625 | - `fn`: function to wrap
626 | - `n`: number of arguments to memoize; if omitted, tries to detect the arity (`fn.length`) to use.
627 |
628 | * **Returns:** *array*
629 |
630 | * **Example:**
631 |
632 | ```js
633 | function sum(x,y) { console.log( "sum called!" ); return x + y; }
634 | function mult({x, y}) { console.log( "mult called!" ); return x * y; }
635 |
636 | var A = FPO.std.memoize( sum );
637 | var B = FPO.std.memoize( sum, 1 ); // be careful here!
638 | var C = FPO.std.memoize( mult, 1 ); // 1 refers to the single object, not its property count
639 |
640 | A( 2, 3 );
641 | // sum called!
642 | // 5
643 |
644 | A( 2, 3 ); // no need to re-compute, value pulled from cache
645 | // 5
646 |
647 | B( 2, 3 ); // different instance, separate cache, re-computed
648 | // sum called!
649 | // 5
650 |
651 | B( 2, 100 ); // oops, memoization fail here!
652 | // 5
653 |
654 | C( {x: 3, y: 4} );
655 | // mult called!
656 | // 12
657 |
658 | C( {x: 3, y: 4} );
659 | // 12
660 |
661 | C( {y: 4, x: 3} ); // oops, cache hashing is different
662 | // mult called!
663 | // 12
664 | ```
665 |
666 | ----
667 |
668 | ### `FPO.std.nAry(..)`
669 |
670 | ([back to top](#standard-api))
671 |
672 | Wraps a function to restrict its inputs to only the count specified.
673 |
674 | * **Arguments:**
675 | - `fn`: function to wrap
676 | - `n`: desired arity count; if omitted, defaults to 0, which produces a "nullary" function -- won't receive any arguments.
677 |
678 | * **Returns:** *function*
679 |
680 | * **Example:**
681 |
682 | ```js
683 | function foo(...args) { return args; }
684 |
685 | var f = FPO.std.nAry( foo, 3 );
686 |
687 | f( 1, 2, 3, 4 ); // [1,2,3]
688 | ```
689 |
690 | * **See Also:** [`FPO.std.binary(..)`](#fpostdbinary), [`FPO.std.unary(..)`](#fpostdunary)
691 |
692 | ----
693 |
694 | ### `FPO.std.partial(..)`
695 |
696 | ([back to top](#standard-api))
697 |
698 | Wraps a function with a new function that already has some of the arguments pre-specified, and is waiting for the rest of them on the next call. Unlike [`FPO.std.curry(..)`](#fpostdcurry), you must specify all the remaining arguments on the next call of the partially-applied function.
699 |
700 | Partial application happens from left-to-right. If you'd need to partially-apply from the right, use [`FPO.std.partialRight(..)`](#fpostdpartialright)
701 |
702 | * **Arguments:**
703 | - `fn`: function to partially-apply
704 | - `args`: array containing the arguments to apply now
705 |
706 | * **Returns:** *function*
707 |
708 | * **Example:**
709 |
710 | ```js
711 | function foo(x,y,z) { return x + y + z; }
712 |
713 | var f = FPO.std.partial( foo, ["a"] );
714 |
715 | f( "b", "!" );
716 | // "ab!"
717 | ```
718 |
719 | * **See Also:** [`FPO.std.partialRight(..)`](#fpostdpartialright), [`FPO.std.curry(..)`](#fpostdcurry), [`FPO.std.curryMultiple(..)`](#fpostdcurrymultiple)
720 |
721 | ----
722 |
723 | ### `FPO.std.partialRight(..)`
724 |
725 | ([back to top](#standard-api))
726 |
727 | Wraps a function with a new function that already has some of the arguments pre-specified, and is waiting for the rest of them on the next call. Unlike [`FPO.std.curry(..)`](#fpostdcurry), you must specify all the remaining arguments on the next call of the partially-applied function.
728 |
729 | Right-partial application happens from right-to-left. If you'd like to partially-apply from the left, use [`FPO.std.partial(..)`](#fpostdpartial)
730 |
731 | * **Arguments:**
732 | - `fn`: function to partially-apply
733 | - `args`: array containing the arguments to apply now
734 |
735 | * **Returns:** *function*
736 |
737 | * **Example:**
738 |
739 | ```js
740 | function foo(x,y,z) { return x + y + z; }
741 |
742 | var f = FPO.std.partialRight( foo, ["b","!"] );
743 |
744 | f( "a" );
745 | // "ab!"
746 | ```
747 |
748 | * **See Also:** [`FPO.std.partial(..)`](#fpostdpartial), [`FPO.std.curry(..)`](#fpostdcurry), [`FPO.std.curryMultiple(..)`](#fpostdcurrymultiple)
749 |
750 | ----
751 |
752 | ### `FPO.std.pick(..)`
753 |
754 | ([back to top](#standard-api))
755 |
756 | Returns a new object with only the specified properties from the original object. Includes only properties from the original object.
757 |
758 | * **Arguments:**
759 | - `props`: array of property names to pick from the object; if a property does not exist on the original object, it **is not** added to the new object, unlike [`FPO.std.pickAll(..)`](#fpostdpickall).
760 | - `v`: object to pick properties from
761 |
762 | * **Returns:** *object*
763 |
764 | * **Example:**
765 |
766 | ```js
767 | var obj = { x: 1, y: 2, z: 3 };
768 |
769 | FPO.std.pick( ["x","y","w"], obj );
770 | // { x:1, y:2 }
771 | ```
772 |
773 | * **See Also:** [`FPO.std.pickAll(..)`](#fpostdpickall), [`FPO.std.prop(..)`](#fpostdprop)
774 |
775 | ----
776 |
777 | ### `FPO.std.pickAll(..)`
778 |
779 | ([back to top](#standard-api))
780 |
781 | Returns a new object with only the specified properties from the original object. Includes all specified properties.
782 |
783 | * **Arguments:**
784 | - `props`: array of property names to pick from the object; even if a property does not exist on the original object, it **is** still added to the new object with an `undefined` value, unlike [`FPO.std.pick(..)`](#fpostdpick).
785 | - `v`: object to pick properties from
786 |
787 | * **Returns:** *object*
788 |
789 | * **Example:**
790 |
791 | ```js
792 | var obj = { x: 1, y: 2, z: 3 };
793 |
794 | FPO.std.pickAll( ["x","y","w"], obj );
795 | // { x:1, y:2, w:undefined }
796 | ```
797 |
798 | * **See Also:** [`FPO.std.pick(..)`](#fpostdpick)
799 |
800 | ----
801 |
802 | ### `FPO.std.pipe(..)`
803 |
804 | ([back to top](#standard-api))
805 |
806 | Produces a new function that's the composition of a list of functions. Functions are composed left-to-right (unlike [`FPO.std.compose(..)`](#fpostdcompose)) from the array.
807 |
808 | * **Arguments:**
809 | - `fns`: array of functions
810 |
811 | * **Returns:** *function*
812 |
813 | * **Example:**
814 |
815 | ```js
816 | var f = FPO.std.pipe( [
817 | v => v + "3",
818 | v => v + "2",
819 | v => v + "1"
820 | ] );
821 |
822 | f( "4" ); // "4321"
823 | ```
824 |
825 | * **Aliases:** `FPO.std.flow(..)`, `FPO.std.sequence(..)`
826 |
827 | * **See Also:** [`FPO.std.compose(..)`](#fpostdcompose)
828 |
829 | ----
830 |
831 | ### `FPO.std.prop(..)`
832 |
833 | ([back to top](#standard-api))
834 |
835 | Extracts a property's value from an object.
836 |
837 | * **Arguments:**
838 | - `prop`: property name to pull from the object
839 | - `v`: object to pull the property value from
840 |
841 | * **Returns:** *-any-*
842 |
843 | * **Example:**
844 |
845 | ```js
846 | var obj = { x: 1, y: 2, z: 3 };
847 |
848 | FPO.std.prop( "y", obj ); // 2
849 | ```
850 |
851 | * **See Also:** [`FPO.std.pick(..)`](#fpostdpick), [`FPO.std.setProp(..)`](#fpostdsetprop)
852 |
853 | ----
854 |
855 | ### `FPO.std.reassoc(..)`
856 |
857 | ([back to top](#standard-api))
858 |
859 | Like a mixture between [`FPO.std.pick(..)`](#fpostdpick) and [`FPO.std.setProp(..)`](#fpostdsetprop), creates a new object that has properties remapped from original names to new names. Any properties present on the original object that aren't remapped are copied with the same name.
860 |
861 | * **Arguments:**
862 | - `props`: object whose key/value pairs are `sourceProp: targetProp` remappings
863 | - `v`: object to remap properties from
864 |
865 | * **Returns:** *object*
866 |
867 | * **Example:**
868 |
869 | ```js
870 | var obj = { x: 1, y: 2, z: 3 };
871 |
872 | FPO.std.reassoc( {x: "A", z: "__z__"}, obj );
873 | // { A: 1, __z__: 3, y: 2}
874 | ```
875 |
876 | * **See Also:** [`FPO.std.pick(..)`](#fpostdpick), [`FPO.std.setProp(..)`](#fpostdsetprop)
877 |
878 | ----
879 |
880 | ### `FPO.std.reduce(..)`
881 |
882 | ([back to top](#standard-api))
883 |
884 | Processes a list from left-to-right (unlike [`FPO.std.reduceRight(..)`](#fpostdreduceright)), successively combining (aka "reducing", "folding") two values into one, until the entire list has been reduced to a single value. An initial value for the reduction can optionally be provided.
885 |
886 | * **Arguments:**
887 | - `fn`: reducer function; called with `acc` (accumulator), `v` (value), `i` (index), and `arr` (array) arguments
888 | - `v`: (optional) initial value to use for the reduction; if provided, the first reduction will pass to the reducer the initial value as the `acc` and the first value from the array as `v`. Otherwise, the first reduction has the first value of the array as `acc` and the second value of the array as `v`.
889 | - `arr`: array to reduce
890 |
891 | * **Returns:** *-any-*
892 |
893 | * **Example:**
894 |
895 | ```js
896 | function strConcat(acc,v) { return acc + v; }
897 |
898 | var vowels = ["a","e","i","o","u","y"];
899 |
900 | FPO.std.reduce( strConcat, undefined, vowels );
901 | // "aeiouy"
902 |
903 | FPO.std.reduce( strConcat, "vowels: ", vowels );
904 | // "vowels: aeiouy"
905 | ```
906 |
907 | * **Aliases:** `FPO.std.fold(..)`, `FPO.std.foldL(..)`
908 |
909 | * **See Also:** [`FPO.std.reduceObj(..)`](#fpostdreduceobj), [`FPO.std.reduceRight(..)`](#fpostdreduceright)
910 |
911 | ----
912 |
913 | ### `FPO.std.reduceObj(..)`
914 |
915 | ([back to top](#standard-api))
916 |
917 | Processes an object's properties (in enumeration order), successively combining (aka "reducing", "folding") two values into one, until all the object's properties have been reduced to a single value. An initial value for the reduction can optionally be provided.
918 |
919 | **Note:** Enumeration order of properties is not strictly guaranteed cross-environment. However, it's generally reliable as the order that properties were listed/added to the object in its definition.
920 |
921 | * **Arguments:**
922 | - `fn`: reducer function; called with `acc` (accumulator), `v` (value), `i` (property name), and `o` (object) arguments
923 | - `o`: object to reduce
924 | - `v`: (optional) initial value to use for the reduction; if provided, the first reduction will pass to the reducer the initial value as the `acc` and the first property value (in enumeration order) from the object as `v`. Otherwise, the first reduction has the first property value (in enumeration order) of the object as `acc` and the second property value (in enumeration order) of the object as `v`.
925 |
926 | * **Returns:** *-any-*
927 |
928 | * **Example:**
929 |
930 | ```js
931 | function strConcat(acc,v) { return acc + v; }
932 |
933 | var vowels = {a: "a", b: "e", c: "i", d: "o", e: "u", f: "y"};
934 |
935 | FPO.std.reduceObj( strConcat, undefined, vowels );
936 | // "aeiouy"
937 |
938 | FPO.std.reduceObj( strConcat, "vowels: ", vowels );
939 | // "vowels: aeiouy"
940 | ```
941 |
942 | * **Aliases:** `FPO.std.foldObj(..)`
943 |
944 | * **See Also:** [`FPO.std.reduce(..)`](#fpostdreduce)
945 |
946 | ----
947 |
948 | ### `FPO.std.reduceRight(..)`
949 |
950 | ([back to top](#standard-api))
951 |
952 | Processes a list from right-to-left (unlike [`FPO.std.reduce(..)`](#fpostdreduce)), successively combining (aka "reducing", "folding") two values into one, until the entire list has been reduced to a single value.
953 |
954 | An initial value for the reduction can optionally be provided. If the array is empty, the initial value is returned (or `undefined` if it was omitted).
955 |
956 | * **Arguments:**
957 | - `fn`: reducer function; called with `acc` (accumulator), `v` (value), `i` (index), and `arr` (array) arguments
958 | - `arr`: array to reduce
959 | - `v`: (optional) initial value to use for the reduction; if provided, the first reduction will pass to the reducer the initial value as the `acc` and the first value from the array as `v`. Otherwise, the first reduction has the first value of the array as `acc` and the second value of the array as `v`.
960 |
961 | * **Returns:** *-any-*
962 |
963 | * **Example:**
964 |
965 | ```js
966 | function strConcat(acc,v) { return acc + v; }
967 |
968 | var vowels = ["a","e","i","o","u","y"];
969 |
970 | FPO.std.reduceRight( strConcat, undefined, vowels );
971 | // "yuoiea"
972 |
973 | FPO.std.reduceRight( strConcat, "vowels: ", vowels );
974 | // "vowels: yuoiea"
975 | ```
976 |
977 | * **Aliases:** `FPO.std.foldR(..)`
978 |
979 | * **See Also:** [`FPO.std.reduce(..)`](#fpostdreduce)
980 |
981 | ----
982 |
983 | ### `FPO.std.remap(..)`
984 |
985 | ([back to top](#standard-api))
986 |
987 | Remaps the expected named arguments of a function. This is useful to adapt a function to be used if the arguments passed in will be different than what the function expects.
988 |
989 | A common usecase will be to adapt a function so it's suitable for use as a mapper/predicate/reducer function, or for composition.
990 |
991 | * **Arguments:**
992 | - `fn`: function to remap
993 | - `args`: object whose key/value pairs represent the `origArgName: newArgName` mappings
994 |
995 | * **Returns:** *function*
996 |
997 | * **Example:**
998 |
999 | ```js
1000 | function double({ x }) { return x * 2; }
1001 |
1002 | var f = FPO.std.remap( double, {x: "v"} );
1003 |
1004 | f( {v: 3} );
1005 | // 6
1006 | ```
1007 |
1008 | * **See Also:** [`FPO.std.reassoc(..)`](#fpostdreassoc)
1009 |
1010 | ----
1011 |
1012 | ### `FPO.std.reverseArgs(..)`
1013 |
1014 | ([back to top](#standard-api))
1015 |
1016 | Wraps a function to reverse the order of all received arguments.
1017 |
1018 | * **Arguments:**
1019 | - `fn`: function to wrap
1020 |
1021 | * **Returns:** *function*
1022 |
1023 | * **Example:**
1024 |
1025 | ```js
1026 | function foo(...args) { return args; }
1027 |
1028 | var f = FPO.std.reverseArgs( foo );
1029 |
1030 | f( 1, 2, 3, 4 );
1031 | // [4,3,2,1]
1032 | ```
1033 |
1034 | * **See Also:** [`FPO.std.flip(..)`](#fpostdflip)
1035 |
1036 | ----
1037 |
1038 | ### `FPO.std.setProp(..)`
1039 |
1040 | ([back to top](#standard-api))
1041 |
1042 | Creates a shallow clone of an object, assigning the specified property value to the new object.
1043 |
1044 | * **Arguments:**
1045 | - `prop`: property name where to set the value on the new object
1046 | - `o`: (optional) object to clone; if omitted, defaults to a new empty object
1047 | - `v`: value
1048 |
1049 | * **Returns:** *object*
1050 |
1051 | * **Example:**
1052 |
1053 | ```js
1054 | var obj = { x: 1, y: 2, z: 3 };
1055 |
1056 | FPO.std.setProp( "w", obj, 4 );
1057 | // { x:1, y:2, z:3, w:4 }
1058 |
1059 | obj;
1060 | // { x:1, y:2, z:3 }
1061 | ```
1062 |
1063 | * **Aliases:** `FPO.std.assoc(..)`
1064 |
1065 | * **See Also:** [`FPO.prop(..)`](#fpoprop)
1066 |
1067 | ----
1068 |
1069 | ### `FPO.std.tail(..)`
1070 |
1071 | ([back to top](#standard-api))
1072 |
1073 | Returns everything else in the value except the element as accessed at index 0; basically the inverse of [`FPO.std.head(..)`](#fpostdhead).
1074 |
1075 | * **Arguments:**
1076 | - `v`: array, string, object
1077 |
1078 | * **Returns:** *any*
1079 |
1080 | * **Example:**
1081 |
1082 | ```js
1083 | var nums = [1,2,3,4];
1084 |
1085 | FPO.std.tail( nums );
1086 | // [2,3,4]
1087 |
1088 | FPO.std.tail( [] );
1089 | // []
1090 |
1091 | FPO.std.tail( "abc" );
1092 | // "bc"
1093 |
1094 | FPO.std.tail( {0: 42, 1: 10} );
1095 | // {1: 10}
1096 | ```
1097 |
1098 | * **See Also:** [`FPO.std.head(..)`](#fpostdhead)
1099 |
1100 | ----
1101 |
1102 | ### `FPO.std.take(..)`
1103 |
1104 | ([back to top](#standard-api))
1105 |
1106 | Returns the specified number of elements from the value, starting from the beginning.
1107 |
1108 | * **Arguments:**
1109 | - `v`: array / string
1110 | - `n`: number of elements to take from the beginning of the value; if omitted, defaults to `1`.
1111 |
1112 | * **Returns:** array / string
1113 |
1114 | * **Example:**
1115 |
1116 | ```js
1117 | var nums = [1,2,3,4];
1118 |
1119 | FPO.std.take( nums, 2 );
1120 | // [1,2]
1121 |
1122 | FPO.std.take( nums );
1123 | // [1]
1124 |
1125 | FPO.std.take( "abc", 2 );
1126 | // "ab"
1127 |
1128 | FPO.std.take( null );
1129 | // []
1130 | ```
1131 |
1132 | * **See Also:** [`FPO.std.head(..)`](#fpostdhead)
1133 |
1134 | ----
1135 |
1136 | ### `FPO.std.trampoline(..)`
1137 |
1138 | ([back to top](#standard-api))
1139 |
1140 | Wraps a continuation-returning recursive function in another function that will run it until it no longer returns another continuation function. Trampolines are an alternative to tail calls.
1141 |
1142 | * **Arguments:**
1143 | - `fn`: function to run
1144 |
1145 | * **Returns:** *function*
1146 |
1147 | * **Example:**
1148 |
1149 | ```js
1150 | function sum(total,x) {
1151 | if (x <= 1) return total + x;
1152 | return () => sum( total + x, x - 1 );
1153 | }
1154 |
1155 | var f = FPO.std.trampoline( sum )
1156 |
1157 | f( 0, 5 );
1158 | // 15
1159 | ```
1160 |
1161 | ----
1162 |
1163 | ### `FPO.std.transducers.array(..)`
1164 |
1165 | ([back to top](#standard-api))
1166 |
1167 | A reducer function. For transducing purposes, a combination function that takes an array and a value, and mutates the array by pushing the value onto the end of it. The mutated array is returned.
1168 |
1169 | **This function has side-effects**, for performance reasons. It should be used with caution.
1170 |
1171 | * **Arguments:**
1172 | - `acc`: acculumator
1173 | - `v`: value
1174 |
1175 | * **Returns:** *array*
1176 |
1177 | * **Example:**
1178 |
1179 | ```js
1180 | var arr = [1,2,3];
1181 |
1182 | FPO.std.transducers.array( arr, 4 );
1183 | // [1,2,3,4]
1184 |
1185 | arr;
1186 | // [1,2,3,4] <-- was mutated as a side-effect!
1187 | ```
1188 |
1189 | * **See Also:** [`FPO.std.transducers.booleanAnd(..)`](#fpostdtransducersbooleanand),
1190 | * [`FPO.std.transducers.booleanOr(..)`](#fpostdtransducersbooleanor), [`FPO.std.transducers.default(..)`](#fpostdtransducersdefault), [`FPO.std.transducers.number(..)`](#fpostdtransducersnumber), [`FPO.std.transducers.string(..)`](#fpostdtransducersstring)
1191 |
1192 | ----
1193 |
1194 | ### `FPO.std.transducers.booleanAnd(..)`
1195 |
1196 | ([back to top](#standard-api))
1197 |
1198 | A reducer function. For transducing purposes, a combination function that takes two booleans and *AND*s them together. The result is the logical *AND* of the two values.
1199 |
1200 | * **Arguments:**
1201 | - `acc`: acculumator
1202 | - `v`: value
1203 |
1204 | * **Returns:** *true/false*
1205 |
1206 | * **Example:**
1207 |
1208 | ```js
1209 | FPO.std.transducers.booleanAnd( true, true );
1210 | // true
1211 |
1212 | FPO.std.transducers.booleanAnd( false, true );
1213 | // false
1214 | ```
1215 |
1216 | * **Aliases:** `FPO.std.transducers.boolean(..)`
1217 |
1218 | * **See Also:** [`FPO.std.transducers.array(..)`](#fpostdtransducersarray),
1219 | * [`FPO.std.transducers.booleanOr(..)`](#fpostdtransducersbooleanor), [`FPO.std.transducers.default(..)`](#fpostdtransducersdefault), [`FPO.std.transducers.number(..)`](#fpostdtransducersnumber), [`FPO.std.transducers.string(..)`](#fpostdtransducersstring)
1220 |
1221 | ----
1222 |
1223 | ### `FPO.std.transducers.booleanOr(..)`
1224 |
1225 | ([back to top](#standard-api))
1226 |
1227 | A reducer function. For transducing purposes, a combination function that takes two booleans and *OR*s them together. The result is the logical *OR* of the two values.
1228 |
1229 | * **Arguments:**
1230 | - `acc`: acculumator
1231 | - `v`: value
1232 |
1233 | * **Returns:** *true/false*
1234 |
1235 | * **Example:**
1236 |
1237 | ```js
1238 | FPO.std.transducers.booleanOr( false, true );
1239 | // true
1240 |
1241 | FPO.std.transducers.booleanOr( false, false );
1242 | // false
1243 | ```
1244 |
1245 | * **See Also:** [`FPO.std.transducers.array(..)`](#fpostdtransducersarray),
1246 | * [`FPO.std.transducers.booleanAnd(..)`](#fpostdtransducersbooleanand), [`FPO.std.transducers.default(..)`](#fpostdtransducersdefault), [`FPO.std.transducers.number(..)`](#fpostdtransducersnumber), [`FPO.std.transducers.string(..)`](#fpostdtransducersstring)
1247 |
1248 | ----
1249 |
1250 | ### `FPO.std.transducers.default(..)`
1251 |
1252 | ([back to top](#standard-api))
1253 |
1254 | A reducer function. For transducing purposes, a combination function that's a default placeholder. It returns only the `acc` that's passed to it. The behavior here is almost the same as [`FPO.std.identity(..)`](#fpostdidentity), except that returns `acc` instead of `v`.
1255 |
1256 | * **Arguments:**
1257 | - `acc`: acculumator
1258 | - `v`: value
1259 |
1260 | * **Returns:** *-any-*
1261 |
1262 | * **Example:**
1263 |
1264 | ```js
1265 | FPO.std.transducers.default( 3, 1 );
1266 | // 3
1267 | ```
1268 |
1269 | * **See Also:** [`FPO.std.transducers.array(..)`](#fpostdtransducersarray),
1270 | * [`FPO.std.transducers.booleanAnd(..)`](#fpostdtransducersbooleanand), [`FPO.std.transducers.booleanOr(..)`](#fpostdtransducersbooleanOr), [`FPO.std.transducers.number(..)`](#fpostdtransducersnumber), [`FPO.std.transducers.string(..)`](#fpostdtransducersstring), [`FPO.std.identity(..)`](#fpostdidentity)
1271 |
1272 | ----
1273 |
1274 | ### `FPO.std.transducers.filter(..)`
1275 |
1276 | ([back to top](#standard-api))
1277 |
1278 | For transducing purposes, wraps a predicate function as a filter-transducer. Typically, this filter-transducer is then composed with other filter-transducers and/or map-transducers. The resulting transducer is then passed to [`FPO.std.transducers.transduce(..)`](#fpostdtransducerstransduce).
1279 |
1280 | The filter-transducer is not a reducer itself; it's expecting a combination function (reducer), which will then produce a filter-reducer. So alternately, you can manually create the filter-reducer and use it directly with a regular [`FPO.std.reduce(..)`](#fpostdreduce) reduction.
1281 |
1282 | * **Arguments:**
1283 | - `fn`: predicate function
1284 |
1285 | * **Returns:** *function*
1286 |
1287 | * **Example:**
1288 |
1289 | ```js
1290 | function isOdd(v) { return v % 2 == 1; }
1291 | function arrayPush(acc,v) { acc.push( v ); return acc; }
1292 |
1293 | var nums = [1,2,3,4,5];
1294 |
1295 | var filterTransducer = FPO.std.transducers.filter( isOdd );
1296 |
1297 | FPO.std.transducers.transduce( filterTransducer, arrayPush, [], nums );
1298 | // [1,3,5]
1299 |
1300 | // ******************
1301 |
1302 | var filterReducer = filterTransducer( arrayPush );
1303 |
1304 | filterReducer( [], 3 );
1305 | // [3]
1306 |
1307 | filterReducer( [], 4 );
1308 | // []
1309 |
1310 | FPO.std.reduce( filterReducer, [], nums );
1311 | // [1,3,5]
1312 | ```
1313 |
1314 | * **See Also:** [`FPO.std.transducers.map(..)`](#fpostdtransducersmap)
1315 |
1316 | ----
1317 |
1318 | ### `FPO.std.transducers.into(..)`
1319 |
1320 | ([back to top](#standard-api))
1321 |
1322 | Selects an appropriate combination function (reducer) based on the provided initial value. Then runs [`FPO.std.transducers.transduce(..)`](#fpostdtransducerstransduce) under the covers.
1323 |
1324 | Detects initial values of `boolean`, `number`, `string`, and `array` types, and dispatches to the appropriate combination function accordingly ([`FPO.std.transducers.number(..)`](#fpostdtransducersnumber), etc). **Note:** A `boolean` initial value selects [`FPO.std.transducers.booleanAnd(..)`](#fpostdtransducersboooleanand).
1325 |
1326 | **Note:** When composing transducers, the effective order of operations is reversed from normal composition. Instead of expecting composition to be right-to-left, the effective order will be left-to-right (see below).
1327 |
1328 | * **Arguments:**
1329 | - `fn`: transducer function
1330 | - `v`: initial value for the reduction; also used to select the appropriate combination function (reducer) for the transducing.
1331 | - `arr`: the list for the reduction
1332 |
1333 | * **Returns:** *-any-*
1334 |
1335 | * **Example:**
1336 |
1337 | ```js
1338 | function double(v) { return v * 2; }
1339 | function isOdd(v) { return v % 2 == 1; }
1340 |
1341 | var nums = [1,2,3,4,5];
1342 |
1343 | var transducer = FPO.std.compose( [
1344 | FPO.std.transducers.filter( isOdd ),
1345 | FPO.std.transducers.map( double )
1346 | ] );
1347 |
1348 | FPO.std.transducers.into( transducer, [], nums );
1349 | // [2,6,10]
1350 |
1351 | FPO.std.transducers.into( transducer, 0, nums );
1352 | // 18
1353 |
1354 | FPO.std.transducers.into( transducer, "", nums );
1355 | // "2610"
1356 | ```
1357 |
1358 | * **See Also:** [`FPO.std.transducers.transduce(..)`](#fpostdtransducerstransduce)
1359 |
1360 | ----
1361 |
1362 | ### `FPO.std.transducers.map(..)`
1363 |
1364 | ([back to top](#standard-api))
1365 |
1366 | For transducing purposes, wraps a mapper function as a map-transducer. Typically, this map-transducer is then composed with other filter-transducers and/or map-transducers. The resulting transducer is then passed to [`FPO.std.transducers.transduce(..)`](#fpostdtransducerstransduce).
1367 |
1368 | The map-transducer is not a reducer itself; it's expecting a combination function (reducer), which will then produce a filter-reducer. So alternately, you can manually create the map-reducer and use it directly with a regular [`FPO.std.reduce(..)`](#fpostdreduce) reduction.
1369 |
1370 | * **Arguments:**
1371 | - `fn`: mapper function
1372 |
1373 | * **Returns:** *function*
1374 |
1375 | * **Example:**
1376 |
1377 | ```js
1378 | function double(v) { return v * 2; }
1379 | function arrayPush(acc,v) { acc.push( v ); return acc; }
1380 |
1381 | var nums = [1,2,3,4,5];
1382 |
1383 | var mapTransducer = FPO.std.transducers.map( double );
1384 |
1385 | FPO.std.transducers.transduce( mapTransducer, arrayPush, [], nums );
1386 | // [2,4,6,8,10]
1387 |
1388 | // ******************
1389 |
1390 | var mapReducer = mapTransducer( arrayPush );
1391 |
1392 | mapReducer( [], 3 );
1393 | // [6]
1394 |
1395 | FPO.std.reduce( mapReducer, [], nums );
1396 | // [2,4,6,8,10]
1397 | ```
1398 |
1399 | * **See Also:** [`FPO.std.transducers.filter(..)`](#fpostdtransducersfilter)
1400 |
1401 | ----
1402 |
1403 | ### `FPO.std.transducers.number(..)`
1404 |
1405 | ([back to top](#standard-api))
1406 |
1407 | A reducer function. For transducing purposes, a combination function that adds together the two numbers passed into it. The result is the sum.
1408 |
1409 | * **Arguments:**
1410 | - `acc`: acculumator
1411 | - `v`: value
1412 |
1413 | * **Returns:** *number*
1414 |
1415 | * **Example:**
1416 |
1417 | ```js
1418 | FPO.std.transducers.number( 3, 4 );
1419 | // 7
1420 | ```
1421 |
1422 | * **See Also:** [`FPO.std.transducers.array(..)`](#fpostdtransducersarray),
1423 | * [`FPO.std.transducers.booleanAnd(..)`](#fpostdtransducersbooleanand), [`FPO.std.transducers.booleanOr(..)`](#fpostdtransducersbooleanOr), [`FPO.std.transducers.default(..)`](#fpostdtransducersdefault), [`FPO.std.transducers.string(..)`](#fpostdtransducersstring)
1424 |
1425 | ----
1426 |
1427 | ### `FPO.std.transducers.string(..)`
1428 |
1429 | ([back to top](#standard-api))
1430 |
1431 | A reducer function. For transducing purposes, a combination function that concats the two strings passed into it. The result is the concatenation.
1432 |
1433 | * **Arguments:**
1434 | - `acc`: acculumator
1435 | - `v`: value
1436 |
1437 | * **Returns:** *string*
1438 |
1439 | * **Example:**
1440 |
1441 | ```js
1442 | FPO.std.transducers.string( "hello", "world" );
1443 | // "helloworld"
1444 | ```
1445 |
1446 | * **See Also:** [`FPO.std.transducers.array(..)`](#fpostdtransducersarray),
1447 | * [`FPO.std.transducers.booleanAnd(..)`](#fpostdtransducersbooleanand), [`FPO.std.transducers.booleanOr(..)`](#fpostdtransducersbooleanOr), [`FPO.std.transducers.default(..)`](#fpostdtransducersdefault), [`FPO.std.transducers.number(..)`](#fpostdtransducersnumber)
1448 |
1449 | ----
1450 |
1451 | ### `FPO.std.transducers.transduce(..)`
1452 |
1453 | ([back to top](#standard-api))
1454 |
1455 | Produces a reducer from a specified transducer and combination function. Then runs a reduction on a list, using that reducer, starting with the specified initial value.
1456 |
1457 | **Note:** When composing transducers, the effective order of operations is reversed from normal composition. Instead of expecting composition to be right-to-left, the effective order will be left-to-right (see below).
1458 |
1459 | * **Arguments:**
1460 | - `fn`: transducer function
1461 | - `co`: combination function for the transducer
1462 | - `v`: initial value for the reduction
1463 | - `arr`: the list for the reduction
1464 |
1465 | * **Returns:** *-any-*
1466 |
1467 | * **Example:**
1468 |
1469 | ```js
1470 | function double(v) { return v * 2; }
1471 | function isOdd(v) { return v % 2 == 1; }
1472 | function arrayPush(acc,v) { acc.push( v ); return acc; }
1473 |
1474 | var nums = [1,2,3,4,5];
1475 |
1476 | var transducer = FPO.std.compose( [
1477 | FPO.std.transducers.filter( isOdd ),
1478 | FPO.std.transducers.map( double )
1479 | ] );
1480 |
1481 | FPO.std.transducers.transduce( transducer, arrayPush, [], nums );
1482 | // [2,6,10]
1483 | ```
1484 |
1485 | * **See Also:** [`FPO.std.transducers.into(..)`](#fpostdtransducersinto)
1486 |
1487 | ----
1488 |
1489 | ### `FPO.std.unapply(..)`
1490 |
1491 | ([back to top](#standard-api))
1492 |
1493 | Wraps a function to gather individual positional arguments into an array argument.
1494 |
1495 | * **Arguments:**
1496 | - `fn`: function to wrap
1497 | - `props`: list of property names (strings) to indicate the order to gather individual positional arguments as properties.
1498 |
1499 | * **Returns:** *function*
1500 |
1501 | * **Example:**
1502 |
1503 | ```js
1504 | function foo([ x,y ]) { return x + y; }
1505 |
1506 | var f = FPO.std.unapply( foo );
1507 |
1508 | f( 1, 2 ); // 3
1509 | ```
1510 |
1511 | * **Aliases:** `FPO.std.gather(..)`
1512 |
1513 | * **See Also:** [`FPO.std.apply(..)`](#fpostdapply)
1514 |
1515 | ----
1516 |
1517 | ### `FPO.std.unary(..)`
1518 |
1519 | ([back to top](#standard-api))
1520 |
1521 | Wraps a function to restrict its inputs to only one argument.
1522 |
1523 | * **Arguments:**
1524 | - `fn`: function to wrap
1525 |
1526 | * **Returns:** *function*
1527 |
1528 | * **Example:**
1529 |
1530 | ```js
1531 | function foo(...args) { return args; }
1532 |
1533 | var f = FPO.std.unary( foo );
1534 |
1535 | f( 1, 2, 3 ); // [1]
1536 | ```
1537 |
1538 | * **See Also:** [`FPO.std.nAry(..)`](#fpostdnary), [`FPO.std.binary(..)`](#fpostdbinary)
1539 |
1540 | ----
1541 |
1542 | ### `FPO.std.uncurry(..)`
1543 |
1544 | ([back to top](#standard-api))
1545 |
1546 | Wraps a (strictly) curried function in a new function that accepts all the arguments at once, and provides them one at a time to the underlying curried function.
1547 |
1548 | * **Arguments:**
1549 | - `fn`: function to uncurry
1550 |
1551 | * **Returns:** *function*
1552 |
1553 | * **Example:**
1554 |
1555 | ```js
1556 | function foo(x,y,z) { return x + y + z; }
1557 |
1558 | var f = FPO.std.curry( foo );
1559 |
1560 | var p = FPO.std.uncurry( f );
1561 |
1562 | p( 1, 2, 3 ); // 6
1563 | ```
1564 |
1565 | * **See Also:** [`FPO.std.curryMultiple(..)`](#fpostdcurrymultiple), [`FPO.std.partial(..)`](#fpostdpartial), [`FPO.std.uncurry(..)`](#fpostduncurry)
1566 |
1567 | ----
1568 |
1569 | ### `FPO.std.zip(..)`
1570 |
1571 | ([back to top](#standard-api))
1572 |
1573 | Produces a new array where each element is an array containing the value from that position in each of the respective input arrays. Only processes up to the shorter of the two arrays.
1574 |
1575 | * **Arguments:**
1576 | - `arr1`: the first array
1577 | - `arr2`: the second array
1578 |
1579 | * **Returns:** *array*
1580 |
1581 | * **Example:**
1582 |
1583 | ```js
1584 | var nums1 = [1,3,5,7];
1585 | var nums2 = [2,4,6];
1586 |
1587 | FPO.std.zip( nums1, nums2 );
1588 | // [[1,2],[3,4],[5,6]]
1589 | ```
1590 |
1591 | * **See Also:** [`FPO.std.flatten(..)`](#fpostdflatten)
1592 |
--------------------------------------------------------------------------------