# types.js A library of JavaScript type extensions, types and type utilities. - [types.js](#typesjs) - [Installation](#installation) - [Basic usage](#basic-usage) - [Built-in type extensions](#built-in-type-extensions) - [`Object`](#object) - [`Object.deepKeys(..)`](#objectdeepkeys) - [`Object.copy(..)` (EXPERIMENTAL)](#objectcopy-experimental) - [`Object.flatCopy(..)`](#objectflatcopy) - [`Object.match(..)`](#objectmatch) - [`Object.matchPartial(..)`](#objectmatchpartial) - [`.run(..)`](#objectrun) - [`Object.sort(..)`](#objectsort) - [`Array`](#array) - [`.first(..)` / `.last(..)`](#arrayfirst--arraylast) - [`.compact()`](#arraycompact) - [`.len`](#arraylen) - [`.unique(..)` / `.tailUnique(..)`](#arrayunique--arraytailunique) - [`.cmp(..)`](#arraycmp) - [`.setCmp(..)`](#arraysetcmp) - [`.sortAs(..)`](#arraysortas) - [`.inplaceSortAs(..)`](#arrayinplacesortas) - [`.toKeys(..)`](#arraytokeys) - [`.toMap(..)`](#arraytomap) - [`Array.zip(..)` / `.zip(..)`](#arrayzip--arrayzip) - [Abortable `Array` iteration](#abortable-array-iteration) - [`array.StopIteration`](#arraystopiteration) - [`.smap(..)` / `.sfilter(..)` / `.sreduce(..)` / `.sforEach(..)`](#arraysmap--arraysfilter--arraysreduce--arraysforeach) - [Large `Array` iteration (chunked)](#large-array-iteration-chunked) - [`array.StopIteration`](#arraystopiteration-1) - [`.CHUNK_SIZE`](#arraychunk_size) - [`.mapChunks(..)`](#arraymapchunks) - [`.filterChunks(..)`](#arrayfilterchunks) - [`.reduceChunks(..)`](#arrayreducechunks) - [`Array` (polyfill)](#array-polyfill) - [`.flat()`](#arrayflat) - [`.includes()`](#arrayincludes) - [`Map`](#map) - [`.sort(..)`](#mapsort) - [`Set`](#set) - [`.unite(..)`](#setunite) - [`.intersect(..)`](#setintersect) - [`.subtract(..)`](#setsubtract) - [`.sort(..)`](#setsort) - [`Date`](#date) - [`Date.timeStamp(..)`](#datetimestamp) - [`Date.fromTimeStamp(..)`](#datefromtimestamp) - [`Date.str2ms(..)`](#datestr2ms) - [`.toShortDate(..)`](#datetoshortdate) - [`.getTimeStamp(..)`](#dategettimestamp) - [`.setTimeStamp(..)`](#datesettimestamp) - [`String`](#string) - [`.capitalize()`](#stringcapitalize) - [`RegExp`](#regexp) - [`RegExp.quoteRegExp(..)`](#regexpquoteregexp) - ['Promise'](#promise) - [`Promise.cooperative(..)`](#promisecooperative) - [Containers](#containers) - [`containers.UniqueKeyMap()` (`Map`)](#containersuniquekeymap-map) - [`.set(..)`](#unique-key-mapset) - [`.reset(..)`](#unique-key-mapreset) - [`.rename(..)`](#unique-key-maprename) - [`.orderedRename(..)`](#unique-key-maporderedrename) - [`.unorderedRename(..)`](#unique-key-mapunorderedrename) - [`.keysOf(..)`](#unique-key-mapkeysof) - [`.originalKey(..)`](#unique-key-maporiginalkey) - [`.uniqueKey(..)`](#unique-key-mapuniquekey) - [`.__key_pattern__`](#unique-key-map__key_pattern__) - [`.__unordered_rename__`](#unique-key-map__unordered_rename__) - [Runner](#runner) - [`runner.Queue(..)` / `runner.Queue.run(..)`](#runnerqueue--runnerqueuerun) - [`.state`](#queuestate) - [`.start(..)`](#queuestart) - [`.pause(..)`](#queuepause) - [`.abort(..)`](#queueabort) - [`.on(..)` / `.one(..)`](#queueon--queueone) - [`.off(..)`](#queueoff) - [`.trigger(..)`](#queuetrigger) - [`.taskCompleted(..)` (event)](#queuetaskcompleted-event) - [License](#license) ## Installation ```shell $ npm install -s 'ig-types' ``` ## Basic usage To extend everything: ```javascript require('ig-types') ``` To have access to additional library types and utilities: ```javascript var types = require('ig-types') ``` `types.js` is organized so as to be able to import/extend only specific sub-modules mostly independently so... In case there is a need to only extend a specific constructor just import the module dealing with that constructor (`Array` in this case): ```javascript // require `ig-types/`... require('ig-types/Array') ``` Note that type patching modules are mostly independent. And to import specific library modules only: ```javascript var containers = require('ig-types/containers') ``` ## Built-in type extensions ### `Object` #### `Object.deepKeys(..)` ``` Object.deepKeys() -> ``` Get list of keys from all objects in the prototype chain. This is different from `Object.keys(..)` which only gets _own_ keys from the current object. Example: ```javascript var a = { x: 123 } var b = Object.create(a) b.y = 321 // get own keys of b... Object.keys(b) // -> ['y'] // get all keys accessible from b... Object.deepKeys(b) // -> ['x', 'y'] ``` #### `Object.copy(..)` (EXPERIMENTAL) ``` Object.copy() -> ``` Create a copy of `` This will: - create a blank `` - link `` to the same prototype chain - copy all _own_ keys from `` to `` Note that this will make no attempt to clone object type. _XXX not yet sure how useful this is._ #### `Object.flatCopy(..)` ``` Object.flatCopy() -> ``` Copy all attributes from the prototype chain of `` into ``. #### `Object.match(..)` #### `Object.matchPartial(..)` #### `.run(..)` ``` .run() -> -> ``` Run a function in the context of `` returning either `` itself (if returning `undefined`) or the result. Note that this is accessible from all JavaScript non-primitive objects, i.e. everything that inherits from `Object`. Example: ```javascript var L = [1, 2, 3] .map(function(e){ return e * 2 }) // see if the first element is 1 and prepend 1 if it is not... .run(function(){ if(this[0] != 1){ this.unshift(1) } }) console.log(L) // -> [1, 2, 6, 8] ``` `.run(..)` is also available standalone via: ```shell $ npm install -s object-run ``` For more info see: https://github.com/flynx/object-run.js #### `Object.sort(..)` Sort `` attributes (same as `Array`'s `.sort(..)`) ``` Object.sort() -> ``` Sort `` attributes via `` function. ``` Object.sort(, ) -> ``` Sort `` attributes to the same order of ``. ``` Object.sort(, ) -> ``` Note that this rewrites all the keys of `` thus for very large sets of keys/attributes this may be quite expensive. Note that some keys of `Object` may misbehave in JavaScript, currently keys that are string values of numbers are sorted automatically by _number value_ and are not affected by `.sort(..)`, this affects both _Chrome_ and _Firefox_. Example: ```javascript var o = {x: 0, a: 1, '100':2, '0':3, ' 27 ':4, b:5} // notice that the order is already different to the order of attributes above... Object.keys(o) // -> ['0', '100', 'x', 'a', ' 27 ', 'b'] // '0' and '100' are not affected by .sort(..) while ' 27 ' is... Object.keys(Object.sort(o, ['x', 'a', '100'])) // -> [ '0', '100', 'x', 'a', ' 27 ', 'b' ] ``` ### `Array` #### `.first(..)` / `.last(..)` Get the first/last items of ``. ``` .first() -> .last() -> ``` Set the first/last items of ``. ``` .first() -> .last() -> ``` Note that these do not affect `` length unless setting items on an empty ``. #### `.compact()` ``` .compact() -> ``` Generate a compact `` from a sparse ``, i.e. removing all the empty slots. #### `.len` Number of non-empty slots/elements in ``. This is similar to: ```javascript var L = [,,, 1,, 2, 3,,] // this is the same as L.len... L.compact().length ``` Note that this is different from `.length` in that writing to `.len` has no effect. #### `.unique(..)` / `.tailUnique(..)` Generate an array with all duplicate elements removed. #### `.cmp(..)` ``` .cmp() -> ``` Compare `` to ``. This will return `true` if: - `` === `` or, - lengths are the same and, - values on the same positions are equal. #### `.setCmp(..)` #### `.sortAs(..)` #### `.inplaceSortAs(..)` #### `.toKeys(..)` #### `.toMap(..)` #### `Array.zip(..)` / `.zip(..)` ### Abortable `Array` iteration #### `array.StopIteration` An exception that if raised while iterating via a supporting iterator method will abort further execution and correctly exit. ```javascript var {StopIteration} = require('ig-types/Array') ``` This can be used in two ways: 1) `throw` as-is to simply stop... ```javascript ;[1,2,3,4,5] .smap(function(e){ // simply abort here and now... throw StopIteration }) ``` Since we aborted the iteration without passing any arguments to `StopIteration`, `.smap(..)` will return `undefined`. 2) `throw` an instance and return the argument... ```javascript // this will print "4" -- the value passed to StopIteration... console.log([1,2,3,4,5] .smap(function(e){ if(e > 3){ // NOTE: new is optional here... // ...StopIteratiom is an object.js constructor. throw new StopIteration(e) } })) ``` #### `.smap(..)` / `.sfilter(..)` / `.sreduce(..)` / `.sforEach(..)` Like `Array`'s `.map(..)`, `.filter(..)`, `.reduce(..)` and `.forEach(..)` but with added support for aborting iteration by throwing `StopIteration`. ### Large `Array` iteration (chunked) Iterating over very large `Array` instances in JavaScript can block execution, to avoid this `types.js` implements `.map(..)`/`.filter(..)`/`.reduce(..)` equivalent methods that iterate the array in chunks and do it asynchronously giving the runtime a chance to run in between. In the simplest cases these are almost a drop-in replacements for the equivalent methods but return a promise. ```javascript var a = [1,2,3,4,5] .map(function(e){ return e*2 }) var b ;[1,2,3,4,5] .mapChunks(function(e){ return e*2 }) .then(function(res){ b = res }) // or with await... var c = await [1,2,3,4,5] .mapChunks(function(e){ return e*2 }) ``` These support setting the chunk size (default: `50`) as the first argument: ```javascript var c = await [1,2,3,4,5] .mapChunks(2, function(e){ return e*2 }) ``` #### `array.StopIteration` Like for [`.smap(..)` and friends](#abortable-array-iteration) iteration can be stopped by throwing a `array.StopIteration` and as before there are two ways to go: 1) `throw` as-is to simply stop ```javascript ;[1,2,3,4,5] .mapChunks(function(e){ // simply abort here and now... throw StopIteration }) .catch(function(){ console.log('done.') }) ``` 2) `Throw` an instance and pass a value to `.catch(..)` ```javascript ;[1,2,3,4,5] .mapChunks(function(e){ if(e > 3){ // NOTE: new is optional here... // ...StopIteratiom is an object.js constructor. throw new StopIteration(e) } }) .catch(function(e){ console.log('first value greater than 3:', e) }) ``` #### `.CHUNK_SIZE` The default iteration chunk size. Note that the smaller this is the more _responsive_ the code is, especially in UI applications but there is a small overhead added per chunk. Default value: `50` #### `.mapChunks(..)` ``` .mapChunks() .mapChunks(, ) -> (, , ) -> ``` ``` .mapChunks([, ]) .mapChunks(, [, ]) -> (, , ) -> (, , ) ``` #### `.filterChunks(..)` #### `.reduceChunks(..)` ### `Array` (polyfill) #### `.flat()` #### `.includes()` ### `Map` #### `.sort(..)` ### `Set` #### `.unite(..)` #### `.intersect(..)` #### `.subtract(..)` #### `.sort(..)` ### `Date` #### `Date.timeStamp(..)` #### `Date.fromTimeStamp(..)` #### `Date.str2ms(..)` #### `.toShortDate(..)` #### `.getTimeStamp(..)` #### `.setTimeStamp(..)` ### `String` #### `.capitalize()` ### `RegExp` #### `RegExp.quoteRegExp(..)` ### 'Promise' #### `Promise.cooperative(..)` ## Containers ```javascript var containers = require('ig-types').containers ``` or, to only import containers: ```javascript var containers = require('ig-types/containers') ``` Note that this will also import `ig-types/Map`. ### `containers.UniqueKeyMap()` (`Map`) `UniqueKeyMap` implements a key-value container (i.e. `Map`) that supports and maintains _duplicate_ keys by appending an index to them. The original keys are stored internally thus the renaming mechanics are stable. `UniqueKeyMap` extends the `Map` constructor, so all the usual `Map` methods and properties apply here. To construct an instance: ```javascript var x = new UniqueKeyMap() ``` or: ```javascript // new is optional... var y = UniqueKeyMap() ``` `UniqueKeyMap` supports the same initialization signature as `Map` but treats repeating keys differently. ```javascript var z = UniqueKeyMap([['a', 1], ['a', 2], ['b', 1]]) ``` The second `"a"` item will automatically get re-keyed as `"a (1)"`: ```javascript console.log([...z.keys()]) // -> ['a', 'a (1)', 'b'] ``` Note that `.set(..)` will never rewrite an element: ```javascript z.set('a', 3) console.log([...z.keys()]) // -> ['a', 'a (1)', 'b', 'a (2)'] z.get('a') // -> 1 z.get('a (1)') // -> 2 ``` To get the generated key: ```javascript var k = z.set('a', 4, true) console.log(k) // -> 'a (3)' ``` To explicitly rewrite an item: ```javascript z.reset('a (1)', 4) z.get('a (1)') // -> 4 ``` And we can _rename_ items, i.e. change their key: ```javascript z.rename('a (2)', 'c') console.log([...z.keys()]) // -> ['a', 'a (1)', 'b', 'a (3)', 'c'] ``` For more info on `Map` see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map #### `.set(..)` ``` .reset(, ) -> .reset(, , true) -> ``` Add an `` to ``. If `` already exists then add an index to it to make it unique. Key updating is done via [`.__key_pattern__`](#unique-key-map__key_pattern__). #### `.reset(..)` ``` .reset(, ) -> ``` Explicitly write an `` under `` as-is, this is like `Map`'s `.set(..)`. #### `.rename(..)` ``` .rename(, ) -> .rename(, , true) -> ``` Rename item key from `` to ``. Same mechanics apply as for [`.set(..)`](#unique-key-mapset) for key uniqueness. Note, if [`.__unordered_rename__`](#unique-key-map__unordered_rename__) is `false` (default) this calls [`.orderedRename(..)`](#unique-key-maporderedrename) otherwise [`.unorderedRename(..)`](#unique-key-mapunorderedrename) is called. #### `.orderedRename(..)` #### `.unorderedRename(..)` #### `.keysOf(..)` #### `.originalKey(..)` #### `.uniqueKey(..)` #### `.__key_pattern__` #### `.__unordered_rename__` ## Runner ### `runner.Queue(..)` / `runner.Queue.run(..)` #### `.state` #### `.start(..)` #### `.pause(..)` #### `.abort(..)` #### `.on(..)` / `.one(..)` #### `.off(..)` #### `.trigger(..)` Trigger an event. #### `.taskCompleted(..)` (event) Event, triggered when a task is completed passing in its result. ## License [BSD 3-Clause License](./LICENSE) Copyright (c) 2020, Alex A. Naanou, All rights reserved.