From 01f18c78165e9dec753f5c58ff29ecd420d5ff95 Mon Sep 17 00:00:00 2001 From: Mat Taylor Date: Tue, 6 Jun 2023 12:35:38 -0700 Subject: [PATCH] keyBy refactor --- README.md | 2 +- bench.js | 2 +- docs/api.md | 11 ++++++----- objix.js | 10 +++++----- objix.min.js | 2 +- package.json | 2 +- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index cd30433..f86435f 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ The following methods are availble to all Objects via protoype inheritence, unle | [`contains`](docs/api.md#contains) | Check if `this` contains all entries from another object to a given depth. | | [`eq`](docs/api.md#eq) | Compare key and value identity between `this` and other objects to a given depth | | [`size`](docs/api.md#size) | Return number of entres in `this`. | -| [`keyBy`](docs/api.md#keyBy) | Index an array of objects into `this` using a given key | +| [`keyBy`](docs/api.md#keyBy) | Re-index values of this `this` using a given key path | | [`memo`](docs/api.md#memo) | Memoize `this` as a function with configurable result cache expiration | | [`bind`](docs/api.md#bind) | Assign a function as a method of `this` with optional memoization | | [`log`](docs/api.md#log) | Conditionally write `this` to the console with an optional message | diff --git a/bench.js b/bench.js index 88e434a..6d8aa0b 100644 --- a/bench.js +++ b/bench.js @@ -63,7 +63,7 @@ function report(title, ob) { vanilla: () => { for (let [k,v] of Object.entries(ob)) if (v == 1) return k }, }, KeyBy: { - objix: () => ({}.keyBy([{a:1},{a:2},{a:3}], 'a')), + objix: () => [{a:1},{a:2},{a:3}].keyBy('a'), lodash: () => _.keyBy([{a:1},{a:2},{a:3}], 'a'), }, Equals: { diff --git a/docs/api.md b/docs/api.md index 43fc397..15616fb 100644 --- a/docs/api.md +++ b/docs/api.md @@ -431,16 +431,17 @@ var o = { a: 1, b: 2 }.size() // 2 -## `Object..keyBy(array, key)` +## `Object..keyBy(path)` -Index an array of objects into `this` using the given key, and return `this`. +Re-Index values of this `this` using the given key path, and return `this`.
```javascript -o = {} -o.keyBy([{ a: 'o1' }, { a: 'o2' }, { a: 'o2', b: 1 }], 'a') +var o = [{ a: 'o1' }, { a: 'o2' }, { a: 'o2', b: 1 }].keyBy('a') o // { o1: { a: 'o1' }, o2: [{ a: 'o2', b: 1 }, { a: 'o2' }] +var o = [{ a: { b: { c:'o1' }}}, { a: { b: { c: 'o2' }}}].keyBy('a.b.c') +o // { o1: { a: { b: { c:'o1' }}}, o2: { a: { b: { c: 'o2' }}}} ```
@@ -491,7 +492,7 @@ setTimeout(() => o.nowish(), 1000) // 2022-10-17T00:01:01.565Z ## `Object..log(msg, test, type='log')` -Prints a shallow clone of `this` to the console together with a minute timestamp and an optional msg. +Prints `this.$()` to the console together with a minute timestamp and an optional msg. If a `test` function is provided then logging will only be triggered if the test function returns truthy when called with with `this` as its first argument. Alternative console methods such as 'trace', 'info', 'error' and 'debug' may also be specified. Returns `this`. diff --git a/objix.js b/objix.js index 637e071..c3ec329 100644 --- a/objix.js +++ b/objix.js @@ -94,13 +94,13 @@ const return K(this).length }, - keyBy(a, k) { - a.map(o => this[o[k]] = this[o[k]] ? [o].concat(this[o[k]]) : o) - return this + keyBy(k, r={}, v) { + this.map(o => r[v=o.at(k)] = r[v] ? [o].concat(r[v]) : o) + return r }, at(p) { - return p.split('.').reduce((v,c) => v[c], this) + return this[p] || p.split('.').reduce((v,c) => v[c], this) }, $(s) { @@ -119,7 +119,7 @@ const }, log(m='', f, c='log') { - (!f || f(this)) && console[c](Date().slice(4,24),'-',m,this.clone()) + (!f || f(this)) && console[c](Date().slice(4,24),'-',m,this.$()) return this }, diff --git a/objix.min.js b/objix.min.js index ae548a8..fedb2c4 100644 --- a/objix.min.js +++ b/objix.min.js @@ -1 +1 @@ -const O=Object,C="constructor",K=O.keys,A=O.assign,I=Symbol.Iterator,M={every(t){for(let e in this)if(!t(this[e],e))return!1;return!0},some(t){for(let e in this)if(t(this[e],e))return!0;return!1},map(t,e={}){for(let i in this)e[i]=t(this[i],i);return e},pick(t,e={}){for(let i in this)(t.call?t(this[i],i):t.includes(i))&&(e[i]=this[i]);return e},flatMap(t){return O.fromEntries(K(this).flatMap(e=>t(e,this[e])))},clean(){return this.pick(t=>t)},is(t,e){return e||t!=O?this[C]==t||!e&&this instanceof t:![Number,String,Boolean,Function,Symbol].includes(this[C])},find(t){for(let e in this)if(t.call?t(this[e],e):this[e].eq(t))return e},extend(...t){return A({},...t).map((t,e)=>this[e]??t,this)},delete(...t){for(let e of t)delete this[e];return this},clone(t){return this.is(O)?this[C]==Array?this.map(e=>t&&e?e.clone(t-1):e):this.size()?this.map(e=>t&&e?e.clone(t-1):e,new this[C]):new this[C](this):this.valueOf()},join(...t){let e=A({},this);for(let i of t)K(i).map(t=>e[t]&&=[].concat(e[t],i[t]));return e},split(t=[]){for(let e in this)this[e].map((i,s)=>t[s]?t[s][e]=i:t[s]={[e]:i});return t},same(t){return this.pick((e,i)=>e.eq(t[i]))},diff(t){return this.pick((e,i)=>!e.eq(t[i]))},contains(t,e){return t.every((t,e)=>this[e]?.eq(t))||e&&this.some(i=>i.contains(t,e-1))},eq(t,e){return this==t||t&&this.is(t[C])&&this.size()==t.size()&&!(this-t)&&this.every((i,s)=>i==t[s]||e&&i?.eq(t[s],e-1))},size(){return K(this).length},keyBy(t,e){return t.map(t=>this[t[e]]=this[t[e]]?[t].concat(this[t[e]]):t),this},at(t){return t.split(".").reduce((t,e)=>t[e],this)},$(t){return t?t.is(String)?t.replace(/\${?([\w\.]+)}?/g,(t,e)=>this.at(e).$()):(t.stringify||t)(this):this.$(JSON).replace(/"(\w+)":/g,"$1:")},memo(t){return t?(...e)=>this[e.$()]??=(this.wait(t).then(t=>delete t[e.$()]),this(...e)):this},bind(t,e,i){return def(this,t,(function(...t){return e(...t,this)}).memo(i)),this},log(t="",e,i="log"){return(!e||e(this))&&console[i](Date().slice(4,24),"-",t,this.clone()),this},try(t,e,i,s){try{s=t(this)}catch(r){s=e&&e(r,this)}return i?this:s},new(t){return this._t?new Proxy(this._t.new(t),this._h):A(this.create(),t)},wait(t){return new Promise((e,i)=>t.is(Number)?setTimeout(()=>e(this),1e3*e):(t=t(this,e,i))&&e(t))},trap(t,e,...i){return new Proxy(this,{set(s,r,n){if((!i[0]||i._find(r))&&!t(n,r,s)&&e)throw e+" "+[r,n].$();return s[r]=n},get(t,e){return({_t:t,_h:this})[e]||t[e]}})}};for(let m of["keys","values","entries","create","assign"])M[m]=function(...t){return O[m](this,...t)};let def=(t,e,i)=>(O.defineProperty(t,e,{writable:!0,value:i}),i);for(let m in O.prototype[I]=function(){return this.values()[I]()},M){[m,"_"+m].map(t=>def(O.prototype,t,M[m]));try{module.exports[m]=(t,...e)=>t["_"+m](...e)}catch{}} \ No newline at end of file +const O=Object,C="constructor",K=O.keys,A=O.assign,I=Symbol.iterator,M={every(t){for(let e in this)if(!t(this[e],e))return!1;return!0},some(t){for(let e in this)if(t(this[e],e))return!0;return!1},map(t,e={}){for(let i in this)e[i]=t(this[i],i);return e},pick(t,e={}){for(let i in this)(t.call?t(this[i],i):t.includes(i))&&(e[i]=this[i]);return e},flatMap(t){return O.fromEntries(K(this).flatMap(e=>t(e,this[e])))},clean(){return this.pick(t=>t)},is(t,e){return e||t!=O?this[C]==t||!e&&this instanceof t:![Number,String,Boolean,Function,Symbol].includes(this[C])},find(t){for(let e in this)if(t.call?t(this[e],e):this[e].eq(t))return e},extend(...t){return A({},...t).map((t,e)=>this[e]??t,this)},delete(...t){for(let e of t)delete this[e];return this},clone(t){return this.is(O)?this[C]==Array?this.map(e=>t&&e?e.clone(t-1):e):this.size()?this.map(e=>t&&e?e.clone(t-1):e,new this[C]):new this[C](this):this.valueOf()},join(...t){let e=A({},this);for(let i of t)K(i).map(t=>e[t]&&=[].concat(e[t],i[t]));return e},split(t=[]){for(let e in this)this[e].map((i,s)=>t[s]?t[s][e]=i:t[s]={[e]:i});return t},same(t){return this.pick((e,i)=>e.eq(t[i]))},diff(t){return this.pick((e,i)=>!e.eq(t[i]))},contains(t,e){return t.every((t,e)=>this[e]?.eq(t))||e&&this.some(i=>i.contains(t,e-1))},eq(t,e){return this==t||t&&this.is(t[C])&&this.size()==t.size()&&!(this-t)&&this.every((i,s)=>i==t[s]||e&&i?.eq(t[s],e-1))},size(){return K(this).length},keyBy(t,e={},i){return this.map(s=>e[i=s.at(t)]=e[i]?[s].concat(e[i]):s),e},at(t){return this[t]||t.split(".").reduce((t,e)=>t[e],this)},$(t){return t?t.is(String)?t.replace(/\${?([\w\.]+)}?/g,(t,e)=>this.at(e).$()):(t.stringify||t)(this):this.$(JSON).replace(/"(\w+)":/g,"$1:")},memo(t){return t?(...e)=>this[e.$()]??=(this.wait(t).then(t=>delete t[e.$()]),this(...e)):this},bind(t,e,i){return def(this,t,(function(...t){return e(...t,this)}).memo(i)),this},log(t="",e,i="log"){return(!e||e(this))&&console[i](Date().slice(4,24),"-",t,this.$()),this},try(t,e,i,s){try{s=t(this)}catch(r){s=e&&e(r,this)}return i?this:s},new(t){return this._t?new Proxy(this._t.new(t),this._h):A(this.create(),t)},wait(t){return new Promise((e,i)=>t.is(Number)?setTimeout(()=>e(this),1e3*t):(t=t(this,e,i))&&e(t))},trap(t,e,...i){return new Proxy(this,{set(s,r,n){if((!i[0]||i._find(r))&&!t(n,r,s)&&e)throw e+" "+[r,n].$();return s[r]=n},get(t,e){return({_t:t,_h:this})[e]||t[e]}})}};for(let m of["keys","values","entries","create","assign"])M[m]=function(...t){return O[m](this,...t)};let def=(t,e,i)=>(O.defineProperty(t,e,{writable:!0,value:i}),i);for(let m in O.prototype[I]=function(){return this.values()[I]()},M){[m,"_"+m].map(t=>def(O.prototype,t,M[m]));try{module.exports[m]=(t,...e)=>t["_"+m](...e)}catch{}} \ No newline at end of file diff --git a/package.json b/package.json index 0ceddd9..c9f0ad9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "objix", - "version": "1.14.2", + "version": "1.15.0", "description": "A dangerously convienient, high performance and super lightweight utility (2.7kb) that injects methods into the Object prototype to sugar for many common use cases working with Javascript objects.", "main": "objix.js", "scripts": {