minor fixes, not done yet...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-10-07 07:33:58 +03:00
parent e29ce4583c
commit cdd9a5e2ba
4 changed files with 309 additions and 44 deletions

162
README.md
View File

@ -3,7 +3,9 @@
A library of JavaScript type extensions, types and type utilities.
- [types.js](#typesjs)
- [Built-in type extenstions](#built-in-type-extenstions)
- [Installation](#installation)
- [Basic usage](#basic-usage)
- [Built-in type extensions](#built-in-type-extensions)
- [`Object`](#object)
- [`Object.deepKeys(..)`](#objectdeepkeys)
- [`Object.match(..)`](#objectmatch)
@ -43,13 +45,52 @@ A library of JavaScript type extensions, types and type utilities.
- [`RegExp`](#regexp)
- [`RegExp.quoteRegExp(..)`](#regexpquoteregexp)
- [Containers](#containers)
- [`UniqueKeyMap()` (`Map`)](#uniquekeymap-map)
- [`containers.UniqueKeyMap()` (`Map`)](#containersuniquekeymap-map)
- [`<unique-key-map>.reset(..)`](#unique-key-mapreset)
- [`<unique-key-map>.uniqueKey(..)`](#unique-key-mapuniquekey)
- [`<unique-key-map>.rename(..)`](#unique-key-maprename)
- [`<unique-key-map>.keysOf(..)`](#unique-key-mapkeysof)
- [`<unique-key-map>.__key_pattern__`](#unique-key-map__key_pattern__)
- [License](#license)
## Built-in type extenstions
## Installation
```shell
$ npm install -s 'ig-types'
```
## Basic usage
To extend everything:
```javascript
require('ig-types')
```
To have access to 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:
```javascript
// require `ig-types/<constructor-name>`...
require('ig-types/Array')
```
And to import specific library modules only:
```javascript
var containers = require('ig-types/containers')
```
Note that though mostly independent now some sub-modules may import
others in the future.
## Built-in type extensions
### `Object`
@ -63,6 +104,40 @@ A library of JavaScript type extensions, types and type utilities.
#### `<object>.run(..)`
```
<object>.run(<func>)
-> <object>
-> <other>
```
Run a function in the context of `<object>` returning either `<object>`
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
### `Array`
@ -148,15 +223,81 @@ Generate an array with all duplicate elements removed.
## Containers
### `UniqueKeyMap()` (`Map`)
```javascript
var containers = require('ig-types').containers
```
or, to only import containers:
```javascript
var containers = require('ig-types/containers')
```
`UniqueKeyMap` extends the `Map` constructor.
### `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']
```
XXX
For more info on `Map` see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
#### `<unique-key-map>.reset(..)`
#### `<unique-key-map>.uniqueKey(..)`
@ -165,7 +306,16 @@ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
#### `<unique-key-map>.keysOf(..)`
#### `<unique-key-map>.__key_pattern__`
## License
[BSD 3-Clause License](./LICENSE)
Copyright (c) 2020, Alex A. Naanou,
All rights reserved.
<!-- vim:set ts=4 sw=4 spell : -->

View File

@ -26,10 +26,22 @@ module.UniqueKeyMap = object.Constructor('UniqueKeyMap', Map, {
// ])
//
// XXX should .__keys_index be non-enumerable???
__keys_index: null,
get __keys(){
return (this.__keys_index =
this.__keys_index || new Map()) },
// Format:
// Map([
// [<unique-key>, <orig-key>],
// ...
// ])
//
__reverse_index: null,
get __reverse(){
return (this.__reverse_index =
this.__reverse_index || new Map()) },
// Patter to be used to generate unique key...
__key_pattern__: '$KEY ($COUNT)',
@ -47,39 +59,10 @@ module.UniqueKeyMap = object.Constructor('UniqueKeyMap', Map, {
__unique_key_value__: false,
// NOTE: this will never overwrite a key's value, to overwrite use .reset(..)
set: function(key, elem, return_key=false){
var names
var n
// index
this.__keys.set(elem,
names = this.__keys.get(elem) || new Set())
// key/elem already exists...
if(this.__unique_key_value__
&& names.has(key)){
return return_key ?
key
: this }
names.add(key)
// add the elem with the unique name...
var res = object.parentCall(
UniqueKeyMap.prototype,
'set',
this,
n = this.uniqieKey(key),
elem)
return return_key ?
n
: res },
reset: function(key, elem){
return object.parentCall(UniqueKeyMap.prototype, 'set', this, key, elem) },
delete: function(key){
var s = this.__keys.get(this.get(key))
if(s){
s.delete(key)
s.size == 0
& this.__keys.delete(this.get(key)) }
return object.parentCall(UniqueKeyMap.prototype, 'delete', this, key) },
// helpers...
//
originalKey: function(key){
return this.__reverse.get(key) },
uniqieKey: function(key){
var n = key
var i = 0
@ -89,10 +72,6 @@ module.UniqueKeyMap = object.Constructor('UniqueKeyMap', Map, {
.replace(/\$KEY/, key)
.replace(/\$COUNT/, i) }
return n },
rename: function(from, to, return_key=false){
var e = this.get(from)
this.delete(from)
return this.set(to, e, return_key) },
keysOf: function(elem, mode='original'){
// get unique keys...
if(mode == 'unique'){
@ -104,6 +83,136 @@ module.UniqueKeyMap = object.Constructor('UniqueKeyMap', Map, {
return res }, []) }
// get keys used to set the values...
return [...(this.__keys.get(elem) || [])] },
// NOTE: we do not touch .__keys here as no renaming is ever done...
// XXX this essentially rewrites the whole map, is there a faster/better
// way to do this???
sortKeysAs: function(keys){
var del = object.parent(UniqueKeyMap.prototype, 'delete').bind(this)
var set = object.parent(UniqueKeyMap.prototype, 'set').bind(this)
new Set([...keys, ...this.keys()])
.forEach(function(k){
var v = this.get(k)
del(k)
set(k, v) }.bind(this))
return this },
// NOTE: this will never overwrite a key's value, to overwrite use .reset(..)
set: function(key, elem, return_key=false){
// index...
var names
this.__keys.set(elem,
names = this.__keys.get(elem) || new Set())
// key/elem already exists...
if(this.__unique_key_value__
&& names.has(key)){
return return_key ?
key
: this }
names.add(key)
// add the elem with the unique name...
var n
var res = object.parentCall(
UniqueKeyMap.prototype,
'set',
this,
n = this.uniqieKey(key),
elem)
// reverse index...
this.__reverse.set(n, key)
return return_key ?
n
: res },
// XXX in-place...
// XXX feels odd....
reset: function(key, elem, in_place=false){
// rewrite...
if(this.has(key)){
// remove old elem/key from .__keys...
var o = this.originalKey(key)
var s = this.__keys.get(this.get(key))
s.delete(o)
s.size == 0
&& this.__keys.delete(this.get(key))
// add new elem/key to .__keys...
var n
this.__keys.set(elem, (n = this.__keys.get(elem) || new Set()))
n.add(o)
return object.parentCall(UniqueKeyMap.prototype, 'set', this, key, elem)
// add...
} else {
return this.set(...arguments) } },
// XXX this affects order...
_reset: function(key, elem, return_key=false){
this.delete(key)
return this.set(...arguments) },
/*/
// XXX this will rewrite the whole thing when it does not have to...
_reset: function(key, elem, in_place=false){
var keys = [...this.keys()]
this.delete(key)
var res = this.set(...arguments)
// keep order...
this.sortKeysAs(keys)
return res },
//*/
// XXX BUG: index leak...
// to reproduce:
// u = UniqueKeyMap([ ['a', 1], ['a', 2], ['a', 3] ])
// u.delete('a (1)')
// -> .__keys still contains [2, 'a'] -- should be gone...
// XXX need a way to get the original key for a specific key, in
// this case: 'a (1)' -> 'a'
// ...add a reverse index???
delete: function(key){
var s = this.__keys.get(this.get(key))
if(s){
// XXX will this delete if key is with an index???
//s.delete(key)
s.delete(this.originalKey(key))
this.__reverse.delete(key)
s.size == 0
&& this.__keys.delete(this.get(key)) }
return object.parentCall(UniqueKeyMap.prototype, 'delete', this, key) },
// XXX this affects order...
rename: function(from, to, return_key=false){
var e = this.get(from)
this.delete(from)
return this.set(to, e, return_key) },
// XXX in-place...
// XXX rename to .rename(..)
/*/ XXX this is ugly...
_rename: function(from, to, return_key=false){
var res
for([k, v] of [...this.entries()]){
this.delete(k)
if(k == from){
res = this.set(to, v, return_key)
} else if(k != to) {
this.reset(k, v) } }
return res },
/*/
// XXX do not se how can we avoid rewriting the map if we want to
// keep order...
_rename: function(from, to, return_key=false){
var keys = [...this.keys()]
var e = this.get(from)
this.delete(from)
var n = this.set(to, e, true)
// keep order...
keys.splice(keys.indexOf(from), 1, n)
this.sortKeysAs(keys)
return return_key ?
n
: this },
//*/
})

View File

@ -1,6 +1,6 @@
{
"name": "ig-types",
"version": "2.0.8",
"version": "2.0.9",
"description": "Generic JavaScript types and type extensions...",
"main": "main.js",
"scripts": {

View File

@ -128,6 +128,7 @@ var cases = test.Cases({
},
// containers.js
// XXX .reset(..) and .rename(..) should not affect order...
UniqueKeyMap: function(assert){
var a = assert(containers.UniqueKeyMap(), '')
var b = assert(containers.UniqueKeyMap([]), '')
@ -162,7 +163,12 @@ var cases = test.Cases({
assert(c.keysOf(222).sort().cmp(['b', 'a'].sort()))
var k = [...c.keys()]
assert((n = c.rename('a', 'b', true)) == 'b (1)')
// XXX should .rename(..) affect element order??
//console.log('>>>>>', k, [...c.keys()])
},
})