{"id":23086802,"url":"https://github.com/papirosko/scats","last_synced_at":"2025-04-03T16:19:46.450Z","repository":{"id":45223865,"uuid":"321578595","full_name":"papirosko/scats","owner":"papirosko","description":"Useful scala classes in typescript","archived":false,"fork":false,"pushed_at":"2022-11-04T09:07:13.000Z","size":259,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-24T15:26:24.581Z","etag":null,"topics":["functional-programming","monad","option","scala","typescript","typescript-library"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/papirosko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-12-15T06:49:50.000Z","updated_at":"2022-07-13T07:54:32.000Z","dependencies_parsed_at":"2023-01-21T01:02:33.260Z","dependency_job_id":null,"html_url":"https://github.com/papirosko/scats","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/papirosko%2Fscats","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/papirosko%2Fscats/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/papirosko%2Fscats/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/papirosko%2Fscats/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/papirosko","download_url":"https://codeload.github.com/papirosko/scats/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247033815,"owners_count":20872532,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","monad","option","scala","typescript","typescript-library"],"created_at":"2024-12-16T19:31:11.016Z","updated_at":"2025-04-03T16:19:46.433Z","avatar_url":"https://github.com/papirosko.png","language":"TypeScript","readme":"# scats\nUseful scala classes in typescript\n\n\n# Install\n```shell\nnpm i scats\n```\n\n# Monads\n## Collection\nRepresents the collection of items of some type.\n\n```typescript\nimport {Collection, Nil} from \"scats\";\n\nconst empty = Collection.empty; // empty collection\nconst empty2 = Nil; // empty collection\nconst example = new Collection([1, 2, 3]); // create instance from array\nconst example2 = Collection.fill(3)(idx =\u003e idx + 1); // Collection.of(1, 2, 3)\nconst c = Collection.of(1, 2, 3);\nfor (let el of c) {\n    console.log(el); // 1, 2, 3\n}\nc.slice(2, 5); // Collection.of(3)\nc.map(e =\u003e e + 1); // Collection.of(2, 3, 4)\nCollection.of(1, 2).flatMap(x =\u003e Collection.of(x, x + 1)); // Collection.of(1, 2, 2, 3)\nCollection.of(1, Collection.of(2, 3), 4).flatten\u003cnumber\u003e(); // Collection.of(1, 2, 3, 4)    \nCollection.of(1, 2, 3).get(1); // 2    \nCollection.of(1, 2, 3).toArray; // [1, 2, 3]    \nCollection.of(1, 2, 3).reverse; // Collection.of(3, 2, 1)    \nCollection.of(2, 3, 1).sort((a, b) =\u003e a - b); // Collection.of(1, 2, 3)    \nCollection.of({x: 2}, {x: 1}, {x: 3}).sortBy(el =\u003e el.x); // Collection.of(1, 2, 3)\nCollection.of(1, 2).appended(3); // Collection.of(1, 2, 3)\nCollection.of(1, 2).appendedAll(Collection.of(3, 4)); // Collection.of(1, 2, 3, 4)\nCollection.of(1, 2).prepended(0); // Collection.of(0, 1, 2)\nCollection.of(1, 2).prependedAll(Collection.of(-1, 0)); // Collection.of(-1, 0, 1, 2)\nCollection.of(1, 2).concat(Collection.of(3, 4)); // Collection.of(1, 2, 3, 4)\nCollection.of(1, 2, 2).toSet; // HashSet.of(1, 2)\nCollection.of(1, 2, 2).distinct; // Collection.of(1, 2)\nCollection.of({x: 2}, {x: 1}, {x: 2}).distinctBy(el =\u003e el.x); // Collection.of({x: 2}, {x: 1})\nCollection.of({id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}).toMap(el =\u003e [el.id, el.name]); // HashMap(1 -\u003e 'Alice', 2 -\u003e 'Bob')\nc.sum(identity); // 6. We have to provide identity to convert element to number\nc.take(2); // Collection.of(1, 2)\nc.drop(1); // Collection.of(2, 3);\nc.head; // 1\n```\n\n\n## Option\nRepresents a possibly empty value.\n\n```typescript\nimport {option} from \"scats\";\n\nconst a = null;\nconst b = option(a); // b = none;\n\nconst c = 1;\nconst d = option(c); // d = some(1);\n```\n\n## Try\nRepresents the result of error prone block.\n\n\n```typescript\nimport {Try} from 'scats';\n\nconst okResult = Try(() =\u003e 1); // a = success(1);\nconst failedResult = Try(() =\u003e { throw new Error(2) }); // b = failure(new Error(2));\n\nokResult.toOption; // some(1)\nfailedResult.toOption; // none\n\nokResult.toEither; // right(1)\nfailedResult.toEither; // left(new Error(2))\nokResult.toEitherMapLeft(error =\u003e 3); // right(1)\nfailedResult.toEitherMapLeft(error =\u003e 3); // left(3)\n\nokResult.map(value =\u003e value + 1); // success(2);\nfailedResult.map(value =\u003e value + 1); // failure(new Error(2));\n\nokResult.isSuccess; // true\nokResult.isFailure; // false\nfailedResult.isSuccess; // false\nfailedResult.isFailure; // true\n\nokResult.match({\n    success: value =\u003e 100,\n    failure: error =\u003e 200\n}); // 100\nfailedResult.match({\n    success: value =\u003e 100,\n    failure: error =\u003e 200\n}); // 200\n\n\nokResult.foreach(value =\u003e console.log(value)); // prints 1\nfailedResult.foreach(value =\u003e console.log(value)); // prints nothing\n\nokResult.tapFailure(error =\u003e console.log(error.message)); // prints nothing\nfailedResult.tapFailure(error =\u003e console.log(error.message)); // prints 2\n\nokResult.recover(error =\u003e 2); // success(1)\nfailedResult.recover(error =\u003e 2); // success(2)\nfailedResult.recover(error =\u003e { throw new Error('fallback'); }); // failure(new Error('fallback'));\n\nokResult.recoverWith(error =\u003e success(2)); // success(1)\nfailedResult.recoverWith(error =\u003e success(2)); // success(2)\nfailedResult.recoverWith(error =\u003e failure(new Error('fallback'))); // failure(new Error('fallback'));\n```\n\nAlso works with promises:\n\n```typescript\nimport {Try} from 'scats';\n\nconst a = await Try.promise(() =\u003e Promise.resolve(1)); // a = success(1);\nawait a.mapPromise(x =\u003e Promise.resolve(x)); // success(1)\n\n\n```\n\n\n\n## Either\nRepresents the value, marked as left or right. Either is right-biased.\n\n```typescript\nimport {left, right, forComprehension} from 'scats';\n\nconst r = right('123'); // Right('123');\nconst l = left('left value'); // Left('123');\nright(123).isRight; // true\nright(123).isLeft; // false\nleft('left value').isRight; // false\nleft('left value').isLeft; // true\n\nright(123).match({\n    right: () =\u003e 'right',\n    left: () =\u003e 'left'\n}); // 'right'\nleft('left value').match({\n    right: () =\u003e 'right',\n    left: () =\u003e 'left'\n}); // 'left'\n\nright(123).fold(\n    leftValue =\u003e console.log(`left: ${leftValue}`),\n    rightValue =\u003e console.log(`right: ${rightValue}`)\n); // 'right: 123'\n\nleft('left value').fold(\n    leftValue =\u003e console.log(`left: ${leftValue}`),\n    rightValue =\u003e console.log(`right: ${rightValue}`)\n); // 'left: left value'\n\nright(123).swap; // left(123);\n\nright(123).foreach(x =\u003e console.log(x)); // 123\nleft(123).foreach(x =\u003e console.log(x)); // prints nothing\n\nright(123).getOrElse(() =\u003e 1); // 123\nright(123).getOrElseValue(1); // 123\nleft(123).getOrElse(() =\u003e 1); // 1\nleft(123).getOrElseValue(1); // 1\n\nright(123).orElse(() =\u003e right(444)); // right(123)\nright(123).orElseValue(right(444)); // right(123)\nleft(123).orElse(() =\u003e right(444)); // right(444)\nleft(123).orElseValue(right(444)); // right(444)\n\nright(123).contains(123); // true\nright(123).contains(124); // false\nleft(123).contains(123); // false\nleft(123).contains(124); // false\n\nright(123).forall(x =\u003e x \u003e= 10); // true\nright(123).forall(x =\u003e x \u003c 10); // false\nleft(123).forall(x =\u003e x \u003e= 10); // false\nleft(123).forall(x =\u003e x \u003c 10); // fales\n\nright(123).exists(x =\u003e x \u003e= 10); // true\nright(123).exists(x =\u003e x \u003c 10); // false\nleft(123).exists(x =\u003e x \u003e= 10); // false\nleft(123).exists(x =\u003e x \u003c 10); // fales\n\nright(123).map(x =\u003e x + 1); // right(124)\nright(123).left.map(x =\u003e x + 1); // right(123)\nleft(123).map(x =\u003e x + 1); // right(123)\nleft(123).left.map(x =\u003e x + 1); // right(124)\n\nright(123).toCollection; // Collection.of(123)\nleft(123).toCollection; // Collection.empty\nright(123).toOption; // Some(123)\nleft(123).toOption; // None\nright(123).toTry; // Success(123)\nleft(123).toTry; // Failure(new Error(123))\n\nright(123).left; // left-biased Either\n\nforComprehension(\n    step('s', () =\u003e left('flower').left),\n    step('m', () =\u003e left('bird').left),\n).yield(({s, m}) =\u003e s.length + m.length); // left(10)\n\n```\n\n## HashMap\n```typescript\nimport {HashMap} from 'scats';\n\nconst map = HashMap.of(['1', 1], ['2', 3]);\nmap.toMap; // Map('1' -\u003e 1, '2' -\u003e 2)\nmap.toArray; // [['1', 1], ['2', 2]]\nmap.size; // 1\nmap.isEmpty; // false\nHashMap.empty.isEmpty; // true\nmap.nonEmpty; // true\nmap.get('1'); // some(1)\nmap.get('5'); // none\nmap.getOrElse('1', () =\u003e 5); // 1\nmap.getOrElse('5', () =\u003e 5); // 5\nmap.keySet; // HashSet.of('1', '2')\nmap.keys; // Collection.of('1', '2')\nmap.values; // Collection.of(1, 2)\nmap.entries; // Collection.of(['1', 1], ['2', 2])\nmap.appendedAll(HashMap.of(['3', 3])); // HashMap.of(['1', 1], ['2', 2], ['3', 3])\nmap.set('2', 3); // HashMap.of(['1', 1], ['2', 3])\nmap.removed('2'); // HashMap.of(['1', 1])\nmap.updated('2', 4); // HashMap.of(['1', 1], ['2', 4])\n```\n\n## HashSet\n\n```typescript\nimport {HashSet} from 'scats';\n\nconst set1 = HashSet.of(1, 2);\nconst set2 = HashSet.of(2, 3);\nset1.size; // 2\nset1.isEmpty; // false\nset1.nonEmpty; // true\nset1.filter(x =\u003e x \u003e 1); // HashSet.of(2)\nset1.filterNot(x =\u003e x \u003e 1); // HashSet.of(1)\nset1.map(x =\u003e x + 1); // HashSet.of(2, 3)\nset1.toCollection; // Collection.of(2, 3) - order may be different\nset1.toMap(x =\u003e [x, x]); // HashMap.of([1, 1], [2, 2])\nset1.contains(1); // true\nset1.appended(2); // HashSet.of(1, 2)\nset1.appendedAll(HashSet.of(1, 2)); // HashSet.of(1, 2)\nset1.removed(1); // HashSet.empty\nset1.removedAll(HashSet.of(1, 2)); // HashSet.empty\nset1.intersect(set2); // HashSet.of(2)\nset1.union(set2); // HashSet.of(1, 2, 3)\n```\n\n## forComprehension\n\n```typescript\nimport {forComprehension, step} from 'scats';\n\nfunction toNum(x: string) {\n    return Try(() =\u003e {\n        const res = parseInt(x);\n        if (isNaN(res)) {\n            throw new Error(`${x} is not a number`);\n        } else {\n            return res;\n        }\n    });\n}\n\n\nforComprehension(\n    step('num1', () =\u003e toNum('1')),\n    step('num2', () =\u003e toNum('2')),\n    step('num3', () =\u003e toNum('3')),\n).yield(({num1, num2, num3}) =\u003e num1 + num2 + num3); // success(6)\n\n\nforComprehension(\n    step('num1', () =\u003e toNum('1')),\n    step('num2', () =\u003e toNum('s2')),\n    step('num3', () =\u003e toNum('3')),\n).yield(({num1, num2, num3}) =\u003e num1 + num2 + num3); // failure(new Error('s2 is not a number')\n\n\n// with collections:\nforComprehension(\n    step('i', () =\u003e Collection.of(1, 2)),\n    step('j', () =\u003e Collection.of(4, 3))\n).yield(({i, j}) =\u003e [i, j]); // Collection.of([1, 4], [1, 3], [2, 4], [2, 3])\n\nforComprehension(\n    step('i', () =\u003e Collection.of(1, 2)),\n    step('j', () =\u003e Collection.of(2, 1)).if(({i, j}) =\u003e i + j === 3)\n).yield(({i, j}) =\u003e [i, j]); // Collection.of([1, 2], [2, 1])\n\n// with promises\n\nfunction toNumPromise(x: string): Promise\u003cTryLike\u003cnumber\u003e\u003e {\n    return Promise.resolve(Try(() =\u003e {\n        const res = parseInt(x);\n        if (isNaN(res)) {\n            throw new Error(`${x} is not a number`);\n        } else {\n            return res;\n        }\n    }));\n}\n\n\nawait forComprehension.promise(\n    task('num1', () =\u003e toNumPromise('1')),\n    task('num2', () =\u003e toNumPromise('2')),\n    task('num3', () =\u003e toNumPromise('3')),\n).yield(({num1, num2, num3}) =\u003e num1 + num2 + num3); // success(6)\n\n\nawait forComprehension.promise(\n    task('num1', () =\u003e toNumPromise('1')),\n    task('num2', () =\u003e toNumPromise('s2')),\n    task('num3', () =\u003e toNumPromise('3')),\n).yield(({num1, num2, num3}) =\u003e num1 + num2 + num3); // failure(new Error('s2 is not a number')\n\nawait forComprehension.promise(\n    task('num1', () =\u003e toNumPromise('1')),\n    task('num2', () =\u003e toNumPromise('2')).if(({num2}) =\u003e num2 \u003e 10),\n    task('num3', () =\u003e toNumPromise('3')),\n).yield(({num1, num2, num3}) =\u003e num1 + num2 + num3); // failure(new Error(\"Predicate does not hold for '2'\")\n\nawait forComprehension.promise(\n    task('num1', () =\u003e toNumPromise('1')),\n    task('num2', () =\u003e { throw new Error('Error in task'); }),\n    task('num3', () =\u003e toNumPromise('3')),\n).yield(({num1, num2, num3}) =\u003e num1 + num2 + num3); // throws Error('Error in task')\n\n```\n\n\n\n## ArrayBuffer\nRepresents the mutable collection of items of some type backed by array.\n\n```typescript\nimport {mutable} from \"scats\";\nimport ArrayBuffer = mutable.ArrayBuffer;\n\nconst empty = ArrayBuffer.empty; // []\nconst example = new ArrayBuffer([1, 2, 3]); // create instance from array\nconst example2 = ArrayBuffer.fill(3)(idx =\u003e idx + 1); // ArrayBuffer.of(1, 2, 3)\nconst c = ArrayBuffer.of(1, 2, 3);\nfor (let el of c) {\n    console.log(el); // 1, 2, 3\n}\nc.slice(2, 5); // ArrayBuffer.of(3)\nc.map(e =\u003e e + 1); // ArrayBuffer.of(2, 3, 4) (new instance)\nArrayBuffer.of(1, 2).flatMap(x =\u003e ArrayBuffer.of(x, x + 1)); // ArrayBuffer.of(1, 2, 2, 3)\nArrayBuffer.of(1, ArrayBuffer.of(2, 3), 4).flatten\u003cnumber\u003e(); // ArrayBuffer.of(1, 2, 3, 4)    \nArrayBuffer.of(1, 2, 3).get(1); // 2    \nArrayBuffer.of(1, 2, 3).toArray; // [1, 2, 3]    \nArrayBuffer.of(1, 2, 3).reverse; // ArrayBuffer.of(3, 2, 1)    \nArrayBuffer.of(2, 3, 1).sort((a, b) =\u003e a - b); // ArrayBuffer.of(1, 2, 3)    \nArrayBuffer.of({x: 2}, {x: 1}, {x: 3}).sortBy(el =\u003e el.x); // ArrayBuffer.of(1, 2, 3)\nArrayBuffer.of(1, 2).append(3); // ArrayBuffer.of(1, 2, 3) - same instance\nArrayBuffer.of(1, 2).appended(3); // ArrayBuffer.of(1, 2, 3) - new instance\nArrayBuffer.of(1, 2).appendAll(ArrayBuffer.of(3, 4)); // ArrayBuffer.of(1, 2, 3, 4) - same instance\nArrayBuffer.of(1, 2).appendAll([3, 4]); // ArrayBuffer.of(1, 2, 3, 4) - same instance\nArrayBuffer.of(1, 2).appendedAll(ArrayBuffer.of(3, 4)); // ArrayBuffer.of(1, 2, 3, 4) - new instance\nArrayBuffer.of(1, 2).prepend(0); // ArrayBuffer.of(0, 1, 2) - same instance\nArrayBuffer.of(1, 2).prepended(0); // ArrayBuffer.of(0, 1, 2) - new instance\nArrayBuffer.of(1, 2).prepended(ArrayBuffer.of(-1, 0)); // ArrayBuffer.of(-1, 0, 1, 2) - same instance\nArrayBuffer.of(1, 2).prependedAll(ArrayBuffer.of(-1, 0)); // ArrayBuffer.of(-1, 0, 1, 2) - new instance\nArrayBuffer.of(1, 2).concat(ArrayBuffer.of(3, 4)); // ArrayBuffer.of(1, 2, 3, 4)\nArrayBuffer.of(1, 2, 2).toSet; // HashSet.of(1, 2)\nArrayBuffer.of(1, 2, 2).distinct; // ArrayBuffer.of(1, 2) - new instance\nArrayBuffer.of({x: 2}, {x: 1}, {x: 2}).distinctBy(el =\u003e el.x); // ArrayBuffer.of({x: 2}, {x: 1}) - new instance\nArrayBuffer.of({id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}).toMap(el =\u003e [el.id, el.name]); // HashMap(1 -\u003e 'Alice', 2 -\u003e 'Bob')\nc.sum(identity); // 6. We have to provide identity to convert element to number\nc.take(2); // Collection.of(1, 2)\nc.drop(1); // Collection.of(2, 3);\nc.head; // 1\n```\n\n\n## mutable.HashMap\n```typescript\nimport {mutable} from \"scats\";\n\nconst map = new mutable.HashMap\u003cstring, number\u003e()\nmap.set('Alice', 11); // ('Alice' -\u003e 11)\nmap.set('Bob', 12); // ('Alice' -\u003e 11, 'Bob' -\u003e 12)\nmap.clear(); // ()\nmap.put('Steve', 14); // returns some(14), map: ('Steve' -\u003e 14)\nmap.update('Alice', 11); // ('Steve' -\u003e 14, 'Alice' -\u003e 11)\nmap.remove('Alice'); // ('Steve' -\u003e 14)\nmap.addOne(['Bob', 12]); // ('Steve' -\u003e 14, 'Bob' -\u003e 12)\nmap.subtractOne('Bob'); // ('Steve' -\u003e 14)\nmap.addAll([['Alice', 11], ['Bob', 12]]); // ('Steve' -\u003e 14, 'Alice' -\u003e 11, 'Bob' -\u003e 12)\nmap.mapValuesInPlace(([name, age]) =\u003e age + 1); // ('Steve' -\u003e 15, 'Alice' -\u003e 12, 'Bob' -\u003e 13)\nmap.filterInPlace(([name, age]) =\u003e age \u003e 13); // ('Steve' -\u003e 15)\n```\n\n\n## mutable.HashSet\n```typescript\nimport {mutable} from \"scats\";\n\nconst set = new mutable.HashSet\u003cstring, number\u003e()\nset.add(1); // true. set = [1]\nset.add(1); // false. set = [1]\nset.clear(); // []]\nset.add(1); // true. set = [1]\nset.remove(2); // false. set = [1]\nset.remove(1); // true. set = []\nset.addAll([2, 3]); // set = [1, 2, 3]\nset.filterInPlace(x =\u003e x \u003e 2); // [3]\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpapirosko%2Fscats","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpapirosko%2Fscats","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpapirosko%2Fscats/lists"}