Skip to content

Commit

Permalink
add set and construct to object module (#236)
Browse files Browse the repository at this point in the history
  • Loading branch information
sodiray authored Jan 16, 2023
1 parent 766347e commit c30ed56
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 44 deletions.
51 changes: 39 additions & 12 deletions cdn/radash.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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];
})()
};
}, {});
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 };
51 changes: 40 additions & 11 deletions cdn/radash.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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];
})()
};
}, {});
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion cdn/radash.min.js

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions docs/object/construct.mdx
Original file line number Diff line number Diff line change
@@ -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
// }
// ]
// }
```
19 changes: 19 additions & 0 deletions docs/object/set.mdx
Original file line number Diff line number Diff line change
@@ -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 }] }
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export { toFloat, toInt } from './number'
export {
assign,
clone,
construct,
crush,
get,
invert,
Expand All @@ -66,6 +67,7 @@ export {
mapValues,
omit,
pick,
set,
shake,
upperize
} from './object'
Expand Down
82 changes: 69 additions & 13 deletions src/object.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { objectify } from './array'
import { toInt } from './number'
import { isArray, isObject, isPrimitive } from './typed'

type LowercasedKeys<T extends Record<string, any>> = {
Expand Down Expand Up @@ -128,17 +129,20 @@ export const clone = <T>(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]
})

Expand Down Expand Up @@ -209,10 +213,10 @@ export const omit = <T, TKeys extends keyof T>(
*/
export const get = <T, K>(
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
Expand All @@ -224,24 +228,60 @@ export const get = <T, K>(
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 = <T extends object, K>(
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 = <X extends Record<string | symbol | number, any>>(
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)
Expand Down Expand Up @@ -287,3 +327,19 @@ export const crush = <TValue extends object>(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 = <TObject extends object>(obj: TObject): object => {
if (!obj) return {}
return Object.keys(obj).reduce((acc, path) => {
return set(acc, path, (obj as any)[path])
}, {})
}
Loading

1 comment on commit c30ed56

@vercel
Copy link

@vercel vercel bot commented on c30ed56 Jan 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.