From c30ed563411c1d9340c79fd003f4bfaa7b1dc85e Mon Sep 17 00:00:00 2001 From: Ray Epps Date: Sun, 15 Jan 2023 21:30:00 -0700 Subject: [PATCH] add set and construct to object module (#236) --- cdn/radash.esm.js | 51 +++++++++++++++++------ cdn/radash.js | 51 ++++++++++++++++++----- cdn/radash.min.js | 2 +- docs/object/construct.mdx | 38 +++++++++++++++++ docs/object/set.mdx | 19 +++++++++ package.json | 2 +- src/index.ts | 2 + src/object.ts | 82 +++++++++++++++++++++++++++++++------ src/tests/object.test.ts | 86 ++++++++++++++++++++++++++++++++++++--- 9 files changed, 289 insertions(+), 44 deletions(-) create mode 100644 docs/object/construct.mdx create mode 100644 docs/object/set.mdx diff --git a/cdn/radash.esm.js b/cdn/radash.esm.js index 848d1f33..af1abd3a 100644 --- a/cdn/radash.esm.js +++ b/cdn/radash.esm.js @@ -647,8 +647,8 @@ const omit = (obj, keys2) => { { ...obj } ); }; -const get = (value, funcOrPath, defaultValue = null) => { - const segments = funcOrPath.split(/[\.\[\]]/g); +const get = (value, path, defaultValue = null) => { + const segments = path.split(/[\.\[\]]/g); let current = value; for (const key of segments) { if (current === null) @@ -663,20 +663,40 @@ const get = (value, funcOrPath, defaultValue = null) => { return defaultValue; return current; }; -const assign = (a, b) => { - if (!a && !b) +const set = (initial, path, value) => { + if (!initial) return {}; - if (!a) - return b; - if (!b) - return a; - return Object.entries(a).reduce((acc, [key, value]) => { + if (!path || !value) + return initial; + const segments = path.split(/[\.\[\]]/g).filter((x) => !!x.trim()); + const _set = (node) => { + if (segments.length > 1) { + const key = segments.shift(); + const nextIsNum = toInt(segments[0], null) === null ? false : true; + node[key] = node[key] === void 0 ? nextIsNum ? [] : {} : node[key]; + _set(node[key]); + } else { + node[segments[0]] = value; + } + }; + const cloned = clone(initial); + _set(cloned); + return cloned; +}; +const assign = (initial, override) => { + if (!initial && !override) + return {}; + if (!initial) + return override; + if (!override) + return initial; + return Object.entries(initial).reduce((acc, [key, value]) => { return { ...acc, [key]: (() => { if (isObject(value)) - return assign(value, b[key]); - return b[key]; + return assign(value, override[key]); + return override[key]; })() }; }, {}); @@ -706,6 +726,13 @@ const crush = (value) => { (k) => get(value, k) ); }; +const construct = (obj) => { + if (!obj) + return {}; + return Object.keys(obj).reduce((acc, path) => { + return set(acc, path, obj[path]); + }, {}); +}; const random = (min, max) => { return Math.floor(Math.random() * (max - min + 1) + min); @@ -847,4 +874,4 @@ const trim = (str, charsToTrim = " ") => { return str.replace(regex, ""); }; -export { alphabetical, assign, boil, callable, camel, capitalize, chain, clone, cluster, compose, counting, crush, dash, debounce, defer, diff, draw, first, flat, fork, get, group, intersects, invert, isArray, isDate, isEmpty, isEqual, isFloat, isFunction, isInt, isNumber, isObject, isPrimitive, isString, isSymbol, iterate, keys, last, list, listify, lowerize, map, mapEntries, mapKeys, mapValues, max, memo, merge, min, objectify, omit, parallel, partial, partob, pascal, pick, proxied, random, range, reduce, replace, replaceOrAppend, retry, select, series, shake, shift, shuffle, sift, sleep, snake, sort, sum, template, throttle, title, toFloat, toInt, toggle, trim, tryit as try, tryit, uid, unique, upperize, zip, zipToObject }; +export { alphabetical, assign, boil, callable, camel, capitalize, chain, clone, cluster, compose, construct, counting, crush, dash, debounce, defer, diff, draw, first, flat, fork, get, group, intersects, invert, isArray, isDate, isEmpty, isEqual, isFloat, isFunction, isInt, isNumber, isObject, isPrimitive, isString, isSymbol, iterate, keys, last, list, listify, lowerize, map, mapEntries, mapKeys, mapValues, max, memo, merge, min, objectify, omit, parallel, partial, partob, pascal, pick, proxied, random, range, reduce, replace, replaceOrAppend, retry, select, series, set, shake, shift, shuffle, sift, sleep, snake, sort, sum, template, throttle, title, toFloat, toInt, toggle, trim, tryit as try, tryit, uid, unique, upperize, zip, zipToObject }; diff --git a/cdn/radash.js b/cdn/radash.js index b37f204e..2a39a121 100644 --- a/cdn/radash.js +++ b/cdn/radash.js @@ -650,8 +650,8 @@ var radash = (function (exports) { { ...obj } ); }; - const get = (value, funcOrPath, defaultValue = null) => { - const segments = funcOrPath.split(/[\.\[\]]/g); + const get = (value, path, defaultValue = null) => { + const segments = path.split(/[\.\[\]]/g); let current = value; for (const key of segments) { if (current === null) @@ -666,20 +666,40 @@ var radash = (function (exports) { return defaultValue; return current; }; - const assign = (a, b) => { - if (!a && !b) + const set = (initial, path, value) => { + if (!initial) return {}; - if (!a) - return b; - if (!b) - return a; - return Object.entries(a).reduce((acc, [key, value]) => { + if (!path || !value) + return initial; + const segments = path.split(/[\.\[\]]/g).filter((x) => !!x.trim()); + const _set = (node) => { + if (segments.length > 1) { + const key = segments.shift(); + const nextIsNum = toInt(segments[0], null) === null ? false : true; + node[key] = node[key] === void 0 ? nextIsNum ? [] : {} : node[key]; + _set(node[key]); + } else { + node[segments[0]] = value; + } + }; + const cloned = clone(initial); + _set(cloned); + return cloned; + }; + const assign = (initial, override) => { + if (!initial && !override) + return {}; + if (!initial) + return override; + if (!override) + return initial; + return Object.entries(initial).reduce((acc, [key, value]) => { return { ...acc, [key]: (() => { if (isObject(value)) - return assign(value, b[key]); - return b[key]; + return assign(value, override[key]); + return override[key]; })() }; }, {}); @@ -709,6 +729,13 @@ var radash = (function (exports) { (k) => get(value, k) ); }; + const construct = (obj) => { + if (!obj) + return {}; + return Object.keys(obj).reduce((acc, path) => { + return set(acc, path, obj[path]); + }, {}); + }; const random = (min, max) => { return Math.floor(Math.random() * (max - min + 1) + min); @@ -860,6 +887,7 @@ var radash = (function (exports) { exports.clone = clone; exports.cluster = cluster; exports.compose = compose; + exports.construct = construct; exports.counting = counting; exports.crush = crush; exports.dash = dash; @@ -916,6 +944,7 @@ var radash = (function (exports) { exports.retry = retry; exports.select = select; exports.series = series; + exports.set = set; exports.shake = shake; exports.shift = shift; exports.shuffle = shuffle; diff --git a/cdn/radash.min.js b/cdn/radash.min.js index 67adc80c..344d1bfd 100644 --- a/cdn/radash.min.js +++ b/cdn/radash.min.js @@ -1 +1 @@ -var radash=function(s){"use strict";const _=t=>!!t&&t.constructor===Symbol,b=t=>!!t&&t.constructor===Array,k=t=>!!t&&t.constructor===Object,j=t=>t==null||typeof t!="object"&&typeof t!="function",p=t=>!!(t&&t.constructor&&t.call&&t.apply),Z=t=>typeof t=="string"||t instanceof String,q=t=>h(t)&&t%1===0,I=t=>h(t)&&t%1!==0,h=t=>{try{return Number(t)===t}catch{return!1}},z=t=>Object.prototype.toString.call(t)==="[object Date]",U=t=>{if(t===!0||t===!1||t==null)return!0;if(h(t))return t===0;if(z(t))return isNaN(t.getTime());if(p(t)||_(t))return!1;const e=t.length;if(h(e))return e===0;const n=t.size;return h(n)?n===0:Object.keys(t).length===0},P=(t,e)=>{if(Object.is(t,e))return!0;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(t instanceof RegExp&&e instanceof RegExp)return t.toString()===e.toString();if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;const n=Reflect.ownKeys(t),r=Reflect.ownKeys(e);if(n.length!==r.length)return!1;for(let i=0;it.reduce((n,r)=>{const i=e(r);return n[i]||(n[i]=[]),n[i].push(r),n},{});function J(...t){return!t||!t.length?[]:new Array(Math.max(...t.map(({length:e})=>e))).fill([]).map((e,n)=>t.map(r=>r[n]))}function W(t,e){if(!t||!t.length)return{};const n=p(e)?e:b(e)?(r,i)=>e[i]:(r,i)=>e;return t.reduce((r,i,u)=>(r[i]=n(i,u),r),{})}const O=(t,e)=>!t||(t.length??0)===0?null:t.reduce(e),X=(t,e)=>(t||[]).reduce((n,r)=>n+(e?e(r):r),0),Y=(t,e=void 0)=>t?.length>0?t[0]:e,G=(t,e=void 0)=>t?.length>0?t[t.length-1]:e,S=(t,e,n=!1)=>{if(!t)return[];const r=(u,c)=>e(u)-e(c),i=(u,c)=>e(c)-e(u);return t.slice().sort(n===!0?i:r)},H=(t,e,n="asc")=>{if(!t)return[];const r=(u,c)=>`${e(u)}`.localeCompare(e(c)),i=(u,c)=>`${e(c)}`.localeCompare(e(u));return t.slice().sort(n==="desc"?i:r)},Q=(t,e)=>t?t.reduce((n,r)=>{const i=e(r);return n[i]=(n[i]??0)+1,n},{}):{},V=(t,e,n)=>{if(!t)return[];if(!e)return[...t];for(let r=0;rr)=>t.reduce((r,i)=>(r[e(i)]=n(i),r),{}),v=(t,e,n)=>t?t.reduce((r,i,u)=>(n(i,u)&&r.push(e(i,u)),r),[]):[],x=(t,e)=>{const n=e||(r=>r);return O(t,(r,i)=>n(r)>n(i)?r:i)},tt=(t,e)=>{const n=e||(r=>r);return O(t,(r,i)=>n(r){const n=Math.ceil(t.length/e);return new Array(n).fill(null).map((r,i)=>t.slice(i*e,i*e+e))},nt=(t,e)=>{const n=t.reduce((r,i)=>{const u=e?e(i):i;return r[u]||(r[u]=i),r},{});return Object.values(n)};function*A(t,e,n=i=>i,r=1){const i=p(n)?n:()=>n,u=e?t:0,c=e??t;for(let o=u;o<=c&&(yield i(o),!(o+r>c));o+=r);}const C=(t,e,n,r)=>Array.from(A(t,e,n,r)),rt=t=>t.reduce((e,n)=>(e.push(...n),e),[]),it=(t,e,n)=>{if(!t||!e)return!1;const r=n??(u=>u),i=e.reduce((u,c)=>(u[r(c)]=!0,u),{});return t.some(u=>i[r(u)])},B=(t,e)=>t?t.reduce((n,r)=>{const[i,u]=n;return e(r)?[[...i,r],u]:[i,[...u,r]]},[[],[]]):[[],[]],st=(t,e,n)=>!e&&!t?[]:e?t?n?t.reduce((r,i)=>{const u=e.find(c=>n(i)===n(c));return u?r.push(u):r.push(i),r},[]):t:[]:t,ut=(t,e,n)=>{if(!t&&!e)return[];if(!e)return[...t];if(!t)return[e];for(let r=0;r{if(!t&&!e)return[];if(!t)return[e];if(!e)return[...t];const i=n?(o,a)=>n(o,a)===n(e,a):o=>o===e;return t.find(i)?t.filter((o,a)=>!i(o,a)):(r?.strategy??"append")==="append"?[...t,e]:[e,...t]},ot=t=>t?.filter(e=>!!e)??[],L=(t,e,n)=>{let r=n;for(let i=1;i<=t;i++)r=e(r,i);return r},ft=(t,e,n=r=>r)=>{if(!t?.length&&!e?.length)return[];if(t?.length===void 0)return[...e];if(!e?.length)return[...t];const r=e.reduce((i,u)=>(i[n(u)]=!0,i),{});return t.filter(i=>!r[n(i)])};function lt(t,e){if(t.length===0)return t;const n=e%t.length;return n===0?t:[...t.slice(-n,t.length),...t.slice(0,-n)]}const at=async(t,e,n)=>{const r=n!==void 0;if(!r&&t?.length<1)throw new Error("Cannot reduce empty array with no init value");const i=r?t:t.slice(1);let u=r?n:t[0];for(const c of i)u=await e(u,c);return u},dt=async(t,e)=>{if(!t)return[];let n=[],r=0;for(const i of t){const u=await e(i,r++);n.push(u)}return n},gt=async t=>{const e=[],n=(u,c)=>e.push({fn:u,rethrow:c?.rethrow??!1}),[r,i]=await m(t)(n);for(const{fn:u,rethrow:c}of e){const[o]=await m(u)(r);if(c)throw o}if(r)throw r;return i};class ht extends Error{constructor(e=[]){super();const n=e.find(r=>r.name)?.name??"";this.name=`AggregateError(${n}...)`,this.message=`AggregateError with ${e.length} errors`,this.stack=e.find(r=>r.stack)?.stack??this.stack,this.errors=e}}const mt=async(t,e,n)=>{const r=e.map((d,y)=>({index:y,item:d})),i=async d=>{const y=[];for(;;){const f=r.pop();if(!f)return d(y);const[l,g]=await m(n)(f.item);y.push({error:l,result:g,index:f.index})}},u=C(1,t).map(()=>new Promise(i)),c=await Promise.all(u),[o,a]=B(S(c.flat(),d=>d.index),d=>!!d.error);if(o.length>0)throw new ht(o.map(d=>d.error));return a.map(d=>d.result)},wt=async(t,e)=>{const n=t?.times??3,r=t?.delay,i=t?.backoff??null;for(const u of A(1,n)){const[c,o]=await m(e)(a=>{throw{_exited:a}});if(!c)return o;if(c._exited)throw c._exited;if(u===n)throw c;r&&await E(r),i&&await E(i(u))}},E=t=>new Promise(e=>setTimeout(e,t)),m=t=>async(...e)=>{try{return[void 0,await t(...e)]}catch(n){return[n,void 0]}},yt=(...t)=>(...e)=>t.slice(1).reduce((n,r)=>r(n),t[0](...e)),pt=(...t)=>t.reverse().reduce((e,n)=>n(e)),bt=(t,...e)=>(...n)=>t(...e,...n),kt=(t,e)=>n=>t({...e,...n}),Ot=t=>new Proxy({},{get:(e,n)=>t(n)}),At=(t,e,n,r)=>function(...u){const c=n?n(...u):JSON.stringify({args:u}),o=t[c];if(o!==void 0&&(!o.exp||o.exp>new Date().getTime()))return o.value;const a=e(...u);return t[c]={exp:r?new Date().getTime()+r:null,value:a},a},Ct=(t,e={})=>At({},t,e.key??null,e.ttl??null),Et=({delay:t},e)=>{let n,r=!0;const i=(...u)=>{r?(clearTimeout(n),n=setTimeout(()=>{r&&e(...u)},t)):e(...u)};return i.cancel=()=>{r=!1},i.flush=(...u)=>e(...u),i},$t=({interval:t},e)=>{let n=!0;return(...i)=>{n&&(e(...i),n=!1,setTimeout(()=>{n=!0},t))}},Nt=(t,e)=>{const n=()=>{};return new Proxy(Object.assign(n,t),{get:(r,i)=>r[i],set:(r,i,u)=>(r[i]=u,!0),apply:(r,i,u)=>e(Object.assign({},r))(...u)})},_t=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseFloat(t);return isNaN(r)?n:r},jt=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseInt(t);return isNaN(r)?n:r},zt=(t,e=n=>n===void 0)=>t?Object.keys(t).reduce((r,i)=>(e(t[i])||(r[i]=t[i]),r),{}):{},$=(t,e)=>Object.keys(t).reduce((r,i)=>(r[e(i,t[i])]=t[i],r),{}),Pt=(t,e)=>Object.keys(t).reduce((r,i)=>(r[i]=e(t[i],i),r),{}),St=(t,e)=>t?Object.entries(t).reduce((n,[r,i])=>{const[u,c]=e(r,i);return n[u]=c,n},{}):{},Tt=t=>t?Object.keys(t).reduce((n,r)=>(n[t[r]]=r,n),{}):{},Bt=t=>$(t,e=>e.toLowerCase()),Lt=t=>$(t,e=>e.toUpperCase()),Mt=t=>{if(j(t))return t;if(typeof t=="function")return t.bind({});const e=new t.constructor;return Object.getOwnPropertyNames(t).forEach(n=>{e[n]=t[n]}),e},Dt=(t,e)=>{if(!t)return[];const n=Object.entries(t);return n.length===0?[]:n.reduce((r,i)=>(r.push(e(i[0],i[1])),r),[])},Ft=(t,e)=>t?e.reduce((n,r)=>(t.hasOwnProperty(r)&&(n[r]=t[r]),n),{}):{},Rt=(t,e)=>t?!e||e.length===0?t:e.reduce((n,r)=>(delete n[r],n),{...t}):{},M=(t,e,n=null)=>{const r=e.split(/[\.\[\]]/g);let i=t;for(const u of r){if(i===null||i===void 0)return n;u.trim()!==""&&(i=i[u])}return i===void 0?n:i},D=(t,e)=>!t&&!e?{}:t?e?Object.entries(t).reduce((n,[r,i])=>({...n,[r]:(()=>k(i)?D(i,e[r]):e[r])()}),{}):t:e,F=t=>{if(!t)return[];const e=(n,r)=>k(n)?Object.entries(n).flatMap(([i,u])=>e(u,[...r,i])):b(n)?n.flatMap((i,u)=>e(i,[...r,`${u}`])):[r.join(".")];return e(t,[])},Zt=t=>t?T(F(t),e=>e,e=>M(t,e)):{},N=(t,e)=>Math.floor(Math.random()*(e-t+1)+t),qt=t=>{const e=t.length;if(e===0)return null;const n=N(0,e-1);return t[n]},It=t=>t.map(e=>({rand:Math.random(),value:e})).sort((e,n)=>e.rand-n.rand).map(e=>e.value),Ut=(t,e="")=>{const n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"+e;return L(t,r=>r+n.charAt(N(0,n.length-1)),"")},Kt=(t,e=n=>`${n}`)=>{const{indexesByKey:n,itemsByIndex:r}=t.reduce((f,l,g)=>({indexesByKey:{...f.indexesByKey,[e(l)]:g},itemsByIndex:{...f.itemsByIndex,[g]:l}}),{indexesByKey:{},itemsByIndex:{}}),i=(f,l)=>n[e(f)]n[e(f)]>n[e(l)]?f:l,c=()=>r[0],o=()=>r[t.length-1],a=(f,l)=>r[n[e(f)]+1]??l??c(),d=(f,l)=>r[n[e(f)]-1]??l??o();return{min:i,max:u,first:c,last:o,next:a,previous:d,spin:(f,l)=>{if(l===0)return f;const g=Math.abs(l),Vt=g>t.length?g%t.length:g;return C(0,Vt-1).reduce(R=>l>0?a(R):d(R),f)}}},w=t=>{if(!t||t.length===0)return"";const e=t.toLowerCase();return e.substring(0,1).toUpperCase()+e.substring(1,e.length)},Jt=t=>{const e=t?.replace(/([A-Z])+/g,w)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}${r.charAt(0).toUpperCase()}${r.slice(1)}`)},Wt=t=>{const e=t?.replace(/([A-Z])+/g,w).split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}_${r.toLowerCase()}`)},Xt=t=>{const e=t?.replace(/([A-Z])+/g,w)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}-${r.toLowerCase()}`)},Yt=t=>{const e=t?.split(/[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("")},Gt=t=>t?t.split(/(?=[A-Z])|[\.\-\s_]/).map(e=>e.trim()).filter(e=>!!e).map(e=>w(e.toLowerCase())).join(" "):"",Ht=(t,e,n=/\{\{(.+?)\}\}/g)=>Array.from(t.matchAll(n)).reduce((r,i)=>r.replace(i[0],e[i[1]]),t),Qt=(t,e=" ")=>{if(!t)return"";const n=new RegExp(`^[${e}]+|[${e}]+$`,"g");return t.replace(n,"")};return s.alphabetical=H,s.assign=D,s.boil=O,s.callable=Nt,s.camel=Jt,s.capitalize=w,s.chain=yt,s.clone=Mt,s.cluster=et,s.compose=pt,s.counting=Q,s.crush=Zt,s.dash=Xt,s.debounce=Et,s.defer=gt,s.diff=ft,s.draw=qt,s.first=Y,s.flat=rt,s.fork=B,s.get=M,s.group=K,s.intersects=it,s.invert=Tt,s.isArray=b,s.isDate=z,s.isEmpty=U,s.isEqual=P,s.isFloat=I,s.isFunction=p,s.isInt=q,s.isNumber=h,s.isObject=k,s.isPrimitive=j,s.isString=Z,s.isSymbol=_,s.iterate=L,s.keys=F,s.last=G,s.list=C,s.listify=Dt,s.lowerize=Bt,s.map=dt,s.mapEntries=St,s.mapKeys=$,s.mapValues=Pt,s.max=x,s.memo=Ct,s.merge=st,s.min=tt,s.objectify=T,s.omit=Rt,s.parallel=mt,s.partial=bt,s.partob=kt,s.pascal=Yt,s.pick=Ft,s.proxied=Ot,s.random=N,s.range=A,s.reduce=at,s.replace=V,s.replaceOrAppend=ut,s.retry=wt,s.select=v,s.series=Kt,s.shake=zt,s.shift=lt,s.shuffle=It,s.sift=ot,s.sleep=E,s.snake=Wt,s.sort=S,s.sum=X,s.template=Ht,s.throttle=$t,s.title=Gt,s.toFloat=_t,s.toInt=jt,s.toggle=ct,s.trim=Qt,s.try=m,s.tryit=m,s.uid=Ut,s.unique=nt,s.upperize=Lt,s.zip=J,s.zipToObject=W,s}({}); +var radash=function(u){"use strict";const _=t=>!!t&&t.constructor===Symbol,b=t=>!!t&&t.constructor===Array,k=t=>!!t&&t.constructor===Object,z=t=>t==null||typeof t!="object"&&typeof t!="function",p=t=>!!(t&&t.constructor&&t.call&&t.apply),U=t=>typeof t=="string"||t instanceof String,K=t=>h(t)&&t%1===0,J=t=>h(t)&&t%1!==0,h=t=>{try{return Number(t)===t}catch{return!1}},P=t=>Object.prototype.toString.call(t)==="[object Date]",W=t=>{if(t===!0||t===!1||t==null)return!0;if(h(t))return t===0;if(P(t))return isNaN(t.getTime());if(p(t)||_(t))return!1;const e=t.length;if(h(e))return e===0;const n=t.size;return h(n)?n===0:Object.keys(t).length===0},S=(t,e)=>{if(Object.is(t,e))return!0;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(t instanceof RegExp&&e instanceof RegExp)return t.toString()===e.toString();if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;const n=Reflect.ownKeys(t),r=Reflect.ownKeys(e);if(n.length!==r.length)return!1;for(let s=0;st.reduce((n,r)=>{const s=e(r);return n[s]||(n[s]=[]),n[s].push(r),n},{});function Y(...t){return!t||!t.length?[]:new Array(Math.max(...t.map(({length:e})=>e))).fill([]).map((e,n)=>t.map(r=>r[n]))}function G(t,e){if(!t||!t.length)return{};const n=p(e)?e:b(e)?(r,s)=>e[s]:(r,s)=>e;return t.reduce((r,s,c)=>(r[s]=n(s,c),r),{})}const O=(t,e)=>!t||(t.length??0)===0?null:t.reduce(e),H=(t,e)=>(t||[]).reduce((n,r)=>n+(e?e(r):r),0),Q=(t,e=void 0)=>t?.length>0?t[0]:e,V=(t,e=void 0)=>t?.length>0?t[t.length-1]:e,j=(t,e,n=!1)=>{if(!t)return[];const r=(c,i)=>e(c)-e(i),s=(c,i)=>e(i)-e(c);return t.slice().sort(n===!0?s:r)},v=(t,e,n="asc")=>{if(!t)return[];const r=(c,i)=>`${e(c)}`.localeCompare(e(i)),s=(c,i)=>`${e(i)}`.localeCompare(e(c));return t.slice().sort(n==="desc"?s:r)},x=(t,e)=>t?t.reduce((n,r)=>{const s=e(r);return n[s]=(n[s]??0)+1,n},{}):{},tt=(t,e,n)=>{if(!t)return[];if(!e)return[...t];for(let r=0;rr)=>t.reduce((r,s)=>(r[e(s)]=n(s),r),{}),et=(t,e,n)=>t?t.reduce((r,s,c)=>(n(s,c)&&r.push(e(s,c)),r),[]):[],nt=(t,e)=>{const n=e||(r=>r);return O(t,(r,s)=>n(r)>n(s)?r:s)},rt=(t,e)=>{const n=e||(r=>r);return O(t,(r,s)=>n(r){const n=Math.ceil(t.length/e);return new Array(n).fill(null).map((r,s)=>t.slice(s*e,s*e+e))},ut=(t,e)=>{const n=t.reduce((r,s)=>{const c=e?e(s):s;return r[c]||(r[c]=s),r},{});return Object.values(n)};function*A(t,e,n=s=>s,r=1){const s=p(n)?n:()=>n,c=e?t:0,i=e??t;for(let o=c;o<=i&&(yield s(o),!(o+r>i));o+=r);}const C=(t,e,n,r)=>Array.from(A(t,e,n,r)),ct=t=>t.reduce((e,n)=>(e.push(...n),e),[]),it=(t,e,n)=>{if(!t||!e)return!1;const r=n??(c=>c),s=e.reduce((c,i)=>(c[r(i)]=!0,c),{});return t.some(c=>s[r(c)])},B=(t,e)=>t?t.reduce((n,r)=>{const[s,c]=n;return e(r)?[[...s,r],c]:[s,[...c,r]]},[[],[]]):[[],[]],ot=(t,e,n)=>!e&&!t?[]:e?t?n?t.reduce((r,s)=>{const c=e.find(i=>n(s)===n(i));return c?r.push(c):r.push(s),r},[]):t:[]:t,ft=(t,e,n)=>{if(!t&&!e)return[];if(!e)return[...t];if(!t)return[e];for(let r=0;r{if(!t&&!e)return[];if(!t)return[e];if(!e)return[...t];const s=n?(o,a)=>n(o,a)===n(e,a):o=>o===e;return t.find(s)?t.filter((o,a)=>!s(o,a)):(r?.strategy??"append")==="append"?[...t,e]:[e,...t]},at=t=>t?.filter(e=>!!e)??[],L=(t,e,n)=>{let r=n;for(let s=1;s<=t;s++)r=e(r,s);return r},dt=(t,e,n=r=>r)=>{if(!t?.length&&!e?.length)return[];if(t?.length===void 0)return[...e];if(!e?.length)return[...t];const r=e.reduce((s,c)=>(s[n(c)]=!0,s),{});return t.filter(s=>!r[n(s)])};function gt(t,e){if(t.length===0)return t;const n=e%t.length;return n===0?t:[...t.slice(-n,t.length),...t.slice(0,-n)]}const ht=async(t,e,n)=>{const r=n!==void 0;if(!r&&t?.length<1)throw new Error("Cannot reduce empty array with no init value");const s=r?t:t.slice(1);let c=r?n:t[0];for(const i of s)c=await e(c,i);return c},mt=async(t,e)=>{if(!t)return[];let n=[],r=0;for(const s of t){const c=await e(s,r++);n.push(c)}return n},wt=async t=>{const e=[],n=(c,i)=>e.push({fn:c,rethrow:i?.rethrow??!1}),[r,s]=await m(t)(n);for(const{fn:c,rethrow:i}of e){const[o]=await m(c)(r);if(i)throw o}if(r)throw r;return s};class yt extends Error{constructor(e=[]){super();const n=e.find(r=>r.name)?.name??"";this.name=`AggregateError(${n}...)`,this.message=`AggregateError with ${e.length} errors`,this.stack=e.find(r=>r.stack)?.stack??this.stack,this.errors=e}}const pt=async(t,e,n)=>{const r=e.map((d,y)=>({index:y,item:d})),s=async d=>{const y=[];for(;;){const f=r.pop();if(!f)return d(y);const[l,g]=await m(n)(f.item);y.push({error:l,result:g,index:f.index})}},c=C(1,t).map(()=>new Promise(s)),i=await Promise.all(c),[o,a]=B(j(i.flat(),d=>d.index),d=>!!d.error);if(o.length>0)throw new yt(o.map(d=>d.error));return a.map(d=>d.result)},bt=async(t,e)=>{const n=t?.times??3,r=t?.delay,s=t?.backoff??null;for(const c of A(1,n)){const[i,o]=await m(e)(a=>{throw{_exited:a}});if(!i)return o;if(i._exited)throw i._exited;if(c===n)throw i;r&&await E(r),s&&await E(s(c))}},E=t=>new Promise(e=>setTimeout(e,t)),m=t=>async(...e)=>{try{return[void 0,await t(...e)]}catch(n){return[n,void 0]}},kt=(...t)=>(...e)=>t.slice(1).reduce((n,r)=>r(n),t[0](...e)),Ot=(...t)=>t.reverse().reduce((e,n)=>n(e)),At=(t,...e)=>(...n)=>t(...e,...n),Ct=(t,e)=>n=>t({...e,...n}),Et=t=>new Proxy({},{get:(e,n)=>t(n)}),$t=(t,e,n,r)=>function(...c){const i=n?n(...c):JSON.stringify({args:c}),o=t[i];if(o!==void 0&&(!o.exp||o.exp>new Date().getTime()))return o.value;const a=e(...c);return t[i]={exp:r?new Date().getTime()+r:null,value:a},a},Nt=(t,e={})=>$t({},t,e.key??null,e.ttl??null),_t=({delay:t},e)=>{let n,r=!0;const s=(...c)=>{r?(clearTimeout(n),n=setTimeout(()=>{r&&e(...c)},t)):e(...c)};return s.cancel=()=>{r=!1},s.flush=(...c)=>e(...c),s},zt=({interval:t},e)=>{let n=!0;return(...s)=>{n&&(e(...s),n=!1,setTimeout(()=>{n=!0},t))}},Pt=(t,e)=>{const n=()=>{};return new Proxy(Object.assign(n,t),{get:(r,s)=>r[s],set:(r,s,c)=>(r[s]=c,!0),apply:(r,s,c)=>e(Object.assign({},r))(...c)})},St=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseFloat(t);return isNaN(r)?n:r},M=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseInt(t);return isNaN(r)?n:r},jt=(t,e=n=>n===void 0)=>t?Object.keys(t).reduce((r,s)=>(e(t[s])||(r[s]=t[s]),r),{}):{},$=(t,e)=>Object.keys(t).reduce((r,s)=>(r[e(s,t[s])]=t[s],r),{}),Tt=(t,e)=>Object.keys(t).reduce((r,s)=>(r[s]=e(t[s],s),r),{}),Bt=(t,e)=>t?Object.entries(t).reduce((n,[r,s])=>{const[c,i]=e(r,s);return n[c]=i,n},{}):{},Lt=t=>t?Object.keys(t).reduce((n,r)=>(n[t[r]]=r,n),{}):{},Mt=t=>$(t,e=>e.toLowerCase()),Dt=t=>$(t,e=>e.toUpperCase()),D=t=>{if(z(t))return t;if(typeof t=="function")return t.bind({});const e=new t.constructor;return Object.getOwnPropertyNames(t).forEach(n=>{e[n]=t[n]}),e},Ft=(t,e)=>{if(!t)return[];const n=Object.entries(t);return n.length===0?[]:n.reduce((r,s)=>(r.push(e(s[0],s[1])),r),[])},Rt=(t,e)=>t?e.reduce((n,r)=>(t.hasOwnProperty(r)&&(n[r]=t[r]),n),{}):{},Zt=(t,e)=>t?!e||e.length===0?t:e.reduce((n,r)=>(delete n[r],n),{...t}):{},F=(t,e,n=null)=>{const r=e.split(/[\.\[\]]/g);let s=t;for(const c of r){if(s===null||s===void 0)return n;c.trim()!==""&&(s=s[c])}return s===void 0?n:s},R=(t,e,n)=>{if(!t)return{};if(!e||!n)return t;const r=e.split(/[\.\[\]]/g).filter(i=>!!i.trim()),s=i=>{if(r.length>1){const o=r.shift(),a=M(r[0],null)!==null;i[o]=i[o]===void 0?a?[]:{}:i[o],s(i[o])}else i[r[0]]=n},c=D(t);return s(c),c},Z=(t,e)=>!t&&!e?{}:t?e?Object.entries(t).reduce((n,[r,s])=>({...n,[r]:(()=>k(s)?Z(s,e[r]):e[r])()}),{}):t:e,I=t=>{if(!t)return[];const e=(n,r)=>k(n)?Object.entries(n).flatMap(([s,c])=>e(c,[...r,s])):b(n)?n.flatMap((s,c)=>e(s,[...r,`${c}`])):[r.join(".")];return e(t,[])},It=t=>t?T(I(t),e=>e,e=>F(t,e)):{},qt=t=>t?Object.keys(t).reduce((e,n)=>R(e,n,t[n]),{}):{},N=(t,e)=>Math.floor(Math.random()*(e-t+1)+t),Ut=t=>{const e=t.length;if(e===0)return null;const n=N(0,e-1);return t[n]},Kt=t=>t.map(e=>({rand:Math.random(),value:e})).sort((e,n)=>e.rand-n.rand).map(e=>e.value),Jt=(t,e="")=>{const n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"+e;return L(t,r=>r+n.charAt(N(0,n.length-1)),"")},Wt=(t,e=n=>`${n}`)=>{const{indexesByKey:n,itemsByIndex:r}=t.reduce((f,l,g)=>({indexesByKey:{...f.indexesByKey,[e(l)]:g},itemsByIndex:{...f.itemsByIndex,[g]:l}}),{indexesByKey:{},itemsByIndex:{}}),s=(f,l)=>n[e(f)]n[e(f)]>n[e(l)]?f:l,i=()=>r[0],o=()=>r[t.length-1],a=(f,l)=>r[n[e(f)]+1]??l??i(),d=(f,l)=>r[n[e(f)]-1]??l??o();return{min:s,max:c,first:i,last:o,next:a,previous:d,spin:(f,l)=>{if(l===0)return f;const g=Math.abs(l),xt=g>t.length?g%t.length:g;return C(0,xt-1).reduce(q=>l>0?a(q):d(q),f)}}},w=t=>{if(!t||t.length===0)return"";const e=t.toLowerCase();return e.substring(0,1).toUpperCase()+e.substring(1,e.length)},Xt=t=>{const e=t?.replace(/([A-Z])+/g,w)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}${r.charAt(0).toUpperCase()}${r.slice(1)}`)},Yt=t=>{const e=t?.replace(/([A-Z])+/g,w).split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}_${r.toLowerCase()}`)},Gt=t=>{const e=t?.replace(/([A-Z])+/g,w)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}-${r.toLowerCase()}`)},Ht=t=>{const e=t?.split(/[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("")},Qt=t=>t?t.split(/(?=[A-Z])|[\.\-\s_]/).map(e=>e.trim()).filter(e=>!!e).map(e=>w(e.toLowerCase())).join(" "):"",Vt=(t,e,n=/\{\{(.+?)\}\}/g)=>Array.from(t.matchAll(n)).reduce((r,s)=>r.replace(s[0],e[s[1]]),t),vt=(t,e=" ")=>{if(!t)return"";const n=new RegExp(`^[${e}]+|[${e}]+$`,"g");return t.replace(n,"")};return u.alphabetical=v,u.assign=Z,u.boil=O,u.callable=Pt,u.camel=Xt,u.capitalize=w,u.chain=kt,u.clone=D,u.cluster=st,u.compose=Ot,u.construct=qt,u.counting=x,u.crush=It,u.dash=Gt,u.debounce=_t,u.defer=wt,u.diff=dt,u.draw=Ut,u.first=Q,u.flat=ct,u.fork=B,u.get=F,u.group=X,u.intersects=it,u.invert=Lt,u.isArray=b,u.isDate=P,u.isEmpty=W,u.isEqual=S,u.isFloat=J,u.isFunction=p,u.isInt=K,u.isNumber=h,u.isObject=k,u.isPrimitive=z,u.isString=U,u.isSymbol=_,u.iterate=L,u.keys=I,u.last=V,u.list=C,u.listify=Ft,u.lowerize=Mt,u.map=mt,u.mapEntries=Bt,u.mapKeys=$,u.mapValues=Tt,u.max=nt,u.memo=Nt,u.merge=ot,u.min=rt,u.objectify=T,u.omit=Zt,u.parallel=pt,u.partial=At,u.partob=Ct,u.pascal=Ht,u.pick=Rt,u.proxied=Et,u.random=N,u.range=A,u.reduce=ht,u.replace=tt,u.replaceOrAppend=ft,u.retry=bt,u.select=et,u.series=Wt,u.set=R,u.shake=jt,u.shift=gt,u.shuffle=Kt,u.sift=at,u.sleep=E,u.snake=Yt,u.sort=j,u.sum=H,u.template=Vt,u.throttle=zt,u.title=Qt,u.toFloat=St,u.toInt=M,u.toggle=lt,u.trim=vt,u.try=m,u.tryit=m,u.uid=Jt,u.unique=ut,u.upperize=Dt,u.zip=Y,u.zipToObject=G,u}({}); diff --git a/docs/object/construct.mdx b/docs/object/construct.mdx new file mode 100644 index 00000000..12e98f3e --- /dev/null +++ b/docs/object/construct.mdx @@ -0,0 +1,38 @@ +--- +title: construct +description: Builds an object from key paths and values +group: Object +--- + +## Basic usage + +The opposite of crush, given an object that was crushed into key paths and values will return the original object reconstructed. + +```ts +import { construct } from 'radash' + +const flat = { + name: 'ra', + power: 100, + 'friend.name': 'loki', + 'friend.power': 80, + 'enemies.0.name': 'hathor', + 'enemies.0.power': 12 +} + +construct(flat) +// { +// name: 'ra', +// power: 100, +// friend: { +// name: 'loki', +// power: 80 +// }, +// enemies: [ +// { +// name: 'hathor', +// power: 12 +// } +// ] +// } +``` diff --git a/docs/object/set.mdx b/docs/object/set.mdx new file mode 100644 index 00000000..722deeb5 --- /dev/null +++ b/docs/object/set.mdx @@ -0,0 +1,19 @@ +--- +title: set +description: Set a value on an object using a path key +group: Object +--- + +## Basic usage + +Opposite of get, dynamically set a nested value into an object using a key path. Does not modify the given initial object. + +```ts +import { set } from 'radash' + +set({}, 'name', 'ra') +// => { name: 'ra' } + +set({}, 'cards[0].value', 2) +// => { cards: [{ value: 2 }] } +``` diff --git a/package.json b/package.json index 22dc066b..9df16018 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "radash", - "version": "10.6.0", + "version": "10.7.0", "description": "Functional utility library - modern, simple, typed, powerful", "main": "dist/cjs/index.cjs", "module": "dist/esm/index.mjs", diff --git a/src/index.ts b/src/index.ts index d33bbfb1..63acf05c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -55,6 +55,7 @@ export { toFloat, toInt } from './number' export { assign, clone, + construct, crush, get, invert, @@ -66,6 +67,7 @@ export { mapValues, omit, pick, + set, shake, upperize } from './object' diff --git a/src/object.ts b/src/object.ts index 82145b48..fbb484ad 100644 --- a/src/object.ts +++ b/src/object.ts @@ -1,4 +1,5 @@ import { objectify } from './array' +import { toInt } from './number' import { isArray, isObject, isPrimitive } from './typed' type LowercasedKeys> = { @@ -128,17 +129,20 @@ export const clone = (obj: T): T => { return obj } - // Binding a function to an empty object creates a copy function. + // Binding a function to an empty object creates a + // copy function. if (typeof obj === 'function') { return obj.bind({}) } - // Access the constructor and create a new object. This method can create an array as well. + // Access the constructor and create a new object. + // This method can create an array as well. const newObj = new ((obj as Object).constructor as { new (): T })() // Assign the props. Object.getOwnPropertyNames(obj).forEach(prop => { - // Bypass type checking since the primitive cases are already checked in the beginning + // Bypass type checking since the primitive cases + // are already checked in the beginning ;(newObj as any)[prop] = (obj as any)[prop] }) @@ -209,10 +213,10 @@ export const omit = ( */ export const get = ( value: T, - funcOrPath: string, + path: string, defaultValue: K | null = null ): K | null => { - const segments = (funcOrPath as string).split(/[\.\[\]]/g) + const segments = path.split(/[\.\[\]]/g) let current: any = value for (const key of segments) { if (current === null) return defaultValue @@ -224,24 +228,60 @@ export const get = ( return current } +/** + * Opposite of get, dynamically set a nested value into + * an object using a key path. Does not modify the given + * initial object. + * + * @example + * set({}, 'name', 'ra') // => { name: 'ra' } + * set({}, 'cards[0].value', 2) // => { cards: [{ value: 2 }] } + */ +export const set = ( + initial: T, + path: string, + value: K +): T => { + if (!initial) return {} as T + if (!path || !value) return initial + const segments = path.split(/[\.\[\]]/g).filter(x => !!x.trim()) + const _set = (node: any) => { + if (segments.length > 1) { + const key = segments.shift() as string + const nextIsNum = toInt(segments[0], null) === null ? false : true + node[key] = node[key] === undefined ? (nextIsNum ? [] : {}) : node[key] + _set(node[key]) + } else { + node[segments[0]] = value + } + } + // NOTE: One day, when structuredClone has more + // compatability use it to clone the value + // https://developer.mozilla.org/en-US/docs/Web/API/structuredClone + const cloned = clone(initial) + _set(cloned) + return cloned +} + /** * Merges two objects together recursivly into a new * object applying values from right to left. * Recursion only applies to child object properties. */ export const assign = >( - a: X, - b: X + initial: X, + override: X ): X => { - if (!a && !b) return {} as X - if (!a) return b as X - if (!b) return a as X - return Object.entries(a).reduce((acc, [key, value]) => { + if (!initial && !override) return {} as X + if (!initial) return override as X + if (!override) return initial as X + return Object.entries(initial).reduce((acc, [key, value]) => { return { ...acc, [key]: (() => { - if (isObject(value)) return assign(value, b[key]) - return b[key] + if (isObject(value)) return assign(value, override[key]) + // if (isArray(value)) return value.map(x => assign) + return override[key] })() } }, {} as X) @@ -287,3 +327,19 @@ export const crush = (value: TValue): object => { k => get(value, k) ) } + +/** + * The opposite of crush, given an object that was + * crushed into key paths and values will return + * the original object reconstructed. + * + * @example + * construct({ name: 'ra', 'children.0.name': 'hathor' }) + * // { name: 'ra', children: [{ name: 'hathor' }] } + */ +export const construct = (obj: TObject): object => { + if (!obj) return {} + return Object.keys(obj).reduce((acc, path) => { + return set(acc, path, (obj as any)[path]) + }, {}) +} diff --git a/src/tests/object.test.ts b/src/tests/object.test.ts index 2c2aaa42..a03374b6 100644 --- a/src/tests/object.test.ts +++ b/src/tests/object.test.ts @@ -360,7 +360,7 @@ describe('object module', () => { }) describe('assign function', () => { - const a = { + const initial = { name: 'jay', cards: ['ac'], location: { @@ -371,7 +371,7 @@ describe('object module', () => { } } } - const b = { + const override = { name: 'charles', cards: ['4c'], location: { @@ -395,8 +395,8 @@ describe('object module', () => { assert.deepEqual(result, { a: 'y' }) }) test('correctly assign a with values from b', () => { - const result = _.assign(a, b) - assert.deepEqual(result, b) + const result = _.assign(initial, override) + assert.deepEqual(result, override) }) }) @@ -432,6 +432,33 @@ describe('object module', () => { }) }) + describe('set function', () => { + test('handles bad input', () => { + assert.deepEqual(_.set({}, '', {}), {}) + assert.deepEqual(_.set({}, null as any, {}), {}) + assert.deepEqual(_.set({}, '', null as any), {}) + assert.deepEqual(_.set(null as any, '', {}), {}) + assert.deepEqual(_.set(null as any, null as any, null as any), {}) + }) + test('sets deep values correctly', () => { + assert.deepEqual(_.set({}, 'cards.value', 2), { + cards: { value: 2 } + }) + assert.deepEqual(_.set({}, 'cards.0.value', 2), { + cards: [{ value: 2 }] + }) + assert.deepEqual(_.set({}, 'cards.0.0.value', 2), { + cards: [[{ value: 2 }]] + }) + assert.deepEqual(_.set({}, 'cards.[0].[0].value', 2), { + cards: [[{ value: 2 }]] + }) + assert.deepEqual(_.set({}, 'cards.[1].[1].value', 2), { + cards: [, [, { value: 2 }]] + }) + }) + }) + describe('crush function', () => { test('handles bad input', () => { assert.deepEqual(_.crush({}), {}) @@ -439,6 +466,7 @@ describe('object module', () => { assert.deepEqual(_.crush(undefined as any), {}) }) test('returns correctly crushed object', () => { + const now = new Date() const ra = { name: 'ra', power: 100, @@ -451,7 +479,8 @@ describe('object module', () => { name: 'hathor', power: 12 } - ] + ], + timestamp: now } assert.deepEqual(_.crush(ra), { name: 'ra', @@ -459,8 +488,53 @@ describe('object module', () => { 'friend.name': 'loki', 'friend.power': 80, 'enemies.0.name': 'hathor', - 'enemies.0.power': 12 + 'enemies.0.power': 12, + timestamp: now }) }) }) + + describe('construct function', () => { + test('handles bad input', () => { + assert.deepEqual(_.construct({}), {}) + assert.deepEqual(_.construct(null as any), {}) + assert.deepEqual(_.construct(undefined as any), {}) + }) + test('returns correctly constructed object', () => { + const now = new Date() + const ra = { + name: 'ra', + power: 100, + friend: { + name: 'loki', + power: 80 + }, + enemies: [ + { + name: 'hathor', + power: 12 + }, + { + name: 'vishnu', + power: 58 + } + ], + timestamp: now + } + assert.deepEqual( + _.construct({ + name: 'ra', + power: 100, + 'friend.name': 'loki', + 'friend.power': 80, + 'enemies.0.name': 'hathor', + 'enemies.0.power': 12, + 'enemies.1.name': 'vishnu', + 'enemies.1.power': 58, + timestamp: now + }), + ra + ) + }) + }) })