Added nop, identity, partial, cmp
This commit is contained in:
commit
5f2a1a0f4b
10 changed files with 10296 additions and 0 deletions
10
.editorconfig
Normal file
10
.editorconfig
Normal file
|
@ -0,0 +1,10 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = off
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
.venv
|
||||
node_modules
|
||||
.rts2_cache_*
|
||||
.coverage
|
||||
/dist/
|
42
README.md
Normal file
42
README.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
tk-functional
|
||||
=============
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
Provides some common helpers for functional-style programming.
|
||||
|
||||
Typescript definitions are included.
|
||||
|
||||
Issues can be reported on [GitLab](https://gitlab.com/thunderk/tk-serializer/issues).
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
**nop** does nothing (useful for some callbacks):
|
||||
|
||||
```typescript
|
||||
new ConstructorWithMandatoryCallback(nop)
|
||||
```
|
||||
|
||||
**identity** returns its argument untouched:
|
||||
|
||||
```typescript
|
||||
a === identity(a) // => true
|
||||
```
|
||||
|
||||
**partial** applies a partial configuration object as first argument of compatible functions:
|
||||
|
||||
```typescript
|
||||
const sum = (args: {a: number, b: number}) => args.a + args.b
|
||||
const plus1 = partial({a: 1}, sum);
|
||||
plus1({b: 8}) // => 9
|
||||
```
|
||||
|
||||
**cmp** simplifies the use of array sorting:
|
||||
|
||||
```typescript
|
||||
[8, 3, 5].sort(cmp()) // => [3, 5, 8]
|
||||
[8, 3, 5].sort(cmp({ reverse: true })) // => [8, 5, 3]
|
||||
[-2, 8, -7].sort(cmp({ key: Math.abs })) // => [-2, -7, 8]
|
||||
```
|
16
activate_node
Normal file
16
activate_node
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Activation script for virtual nodejs environment
|
||||
# Usage:
|
||||
# source activate_node
|
||||
|
||||
vdir="./.venv"
|
||||
expected="10.16.3"
|
||||
|
||||
if [ \! -f "./activate_node" ]
|
||||
then
|
||||
echo "Not in project directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test -x "${vdir}/bin/nodeenv" || ( python3 -m venv "${vdir}" && "${vdir}/bin/pip" install --upgrade nodeenv )
|
||||
test "$(${vdir}/node/bin/nodejs --version)" = "v${expected}" || "${vdir}/bin/nodeenv" --node=${expected} --force "${vdir}/node"
|
||||
source "${vdir}/node/bin/activate"
|
23
jest.config.js
Normal file
23
jest.config.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
module.exports = {
|
||||
transform: {
|
||||
"^.+\\.ts$": "ts-jest"
|
||||
},
|
||||
moduleFileExtensions: [
|
||||
"ts",
|
||||
"js",
|
||||
"json",
|
||||
"node"
|
||||
],
|
||||
restoreMocks: true,
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
"src/**/*.ts",
|
||||
"!src/**/*.test.ts",
|
||||
],
|
||||
coverageDirectory: ".coverage",
|
||||
coverageReporters: [
|
||||
"lcovonly",
|
||||
"html",
|
||||
"text-summary"
|
||||
]
|
||||
}
|
10080
package-lock.json
generated
Normal file
10080
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
29
package.json
Normal file
29
package.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "tk-functional",
|
||||
"version": "1.0.0",
|
||||
"description": "Helpers for functional programming style",
|
||||
"main": "dist/tk-functional.js",
|
||||
"scripts": {
|
||||
"test": "npx tk-base test",
|
||||
"normalize": "npx tk-base normalize",
|
||||
"build": "npx tk-base build",
|
||||
"prepare": "npm run build",
|
||||
"prepublishOnly": "npm test"
|
||||
},
|
||||
"author": {
|
||||
"name": "Michaël Lemaire",
|
||||
"url": "https://thunderk.net"
|
||||
},
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"tk-base": "^0.1.2"
|
||||
},
|
||||
"source": "src/index.ts",
|
||||
"umd:main": "dist/tk-functional.js",
|
||||
"types": "dist/src/index.d.ts",
|
||||
"files": [
|
||||
"/src",
|
||||
"/dist"
|
||||
],
|
||||
"dependencies": {}
|
||||
}
|
37
src/index.test.ts
Normal file
37
src/index.test.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { partial, cmp, nop, identity } from "./index";
|
||||
|
||||
describe("nop", () => {
|
||||
it("does nothing", () => {
|
||||
expect(nop()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("identity", () => {
|
||||
it("returns the argument untouched", () => {
|
||||
expect(identity(5)).toBe(5);
|
||||
expect(identity(null)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("partial", () => {
|
||||
it("applies systematically fixed configuration", () => {
|
||||
const func = (conf: { a: number, b: string, c: number }) => `${conf.a}${conf.b}${conf.c}`;
|
||||
const pfunc = partial({ b: "test" }, func);
|
||||
expect(pfunc({ a: 5, c: 8 })).toEqual("5test8");
|
||||
});
|
||||
|
||||
it("pass through remaining arguments", () => {
|
||||
const func = (conf: { a: number, b: string }, c: number) => `${conf.a}${conf.b}${c}`;
|
||||
const pfunc = partial({ b: "test" }, func);
|
||||
expect(pfunc({ a: 2 }, 3)).toEqual("2test3");
|
||||
});
|
||||
});
|
||||
|
||||
describe("cmp", () => {
|
||||
it("simplifies array sorting", () => {
|
||||
expect([8, 3, 5].sort(cmp())).toEqual([3, 5, 8]);
|
||||
expect([8, 3, 5, 8].sort(cmp({ reverse: true }))).toEqual([8, 8, 5, 3]);
|
||||
expect([-2, 8, -7].sort(cmp({ key: Math.abs }))).toEqual([-2, -7, 8]);
|
||||
expect(["c", "a", "b"].sort(cmp())).toEqual(["a", "b", "c"]);
|
||||
});
|
||||
});
|
39
src/index.ts
Normal file
39
src/index.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Functions that does nothing (useful for default callbacks)
|
||||
*/
|
||||
export function nop(...args: any[]): any {
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity function (returns the sole argument untouched)
|
||||
*/
|
||||
export function identity<T>(input: T): T {
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Partially apply fixed arguments to a function with named arguments
|
||||
*/
|
||||
export function partial<A extends object, PA extends Partial<A>, RA extends any[], R>(fixedargs: PA, func: (args: A, ...rest: RA) => R): (args: Omit<A, keyof PA>, ...rest: RA) => R {
|
||||
return (args: Omit<A, keyof PA>, ...rest: RA) => func({ ...fixedargs, ...args } as any, ...rest);
|
||||
}
|
||||
|
||||
export type cmpArgs = Readonly<{ key: (item: any) => any, reverse: boolean }>
|
||||
export const cmpDefaults: cmpArgs = { key: identity, reverse: false }
|
||||
/**
|
||||
* Compare operator, that can be used in sort() calls.
|
||||
*/
|
||||
export function cmp(args?: Partial<cmpArgs>): (a: any, b: any) => number {
|
||||
const fargs = { ...cmpDefaults, ...args };
|
||||
return (a, b) => {
|
||||
const ka = fargs.key(a);
|
||||
const kb = fargs.key(b);
|
||||
if (ka > kb) {
|
||||
return fargs.reverse ? -1 : 1;
|
||||
} else if (ka < kb) {
|
||||
return fargs.reverse ? 1 : -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"preserveConstEnums": true,
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es6"
|
||||
],
|
||||
"target": "es6"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue