//#region Object
/**
* Flattens an object recursively into one.
* @memberOf object
* @function
* @example
* _$.flattenObj({
hello: "world",
another: {
nested: "Value",
anotherNestedValue: {
"something": "A value"
},
"more Values!!": "lol"
}
}); // { hello: "world", nested: "Value", something: "A value", more Values!!: "lol" }
* @param {Object} o The object to flatten
* @returns {Object} The flattened object.
*/
export let flattenObj = (o = req("object", "object")) => {
return o !== Object(o) || Array.isArray(o)
? {}
: Object.assign(
{},
...(function leaves(o) {
return [].concat.apply(
[],
Object.entries(o).map(([k, v]) => {
return !v ||
typeof v !== "object" ||
!Object.keys(v).some((key) =>
v.hasOwnProperty(key),
) ||
Array.isArray(v)
? { [k]: v }
: leaves(v);
}),
);
})(o),
);
};
/**
* Deep clones an object (or anything else, like an array or string)
* @function
* @memberOf object
* @param {Object|Array|String} src The object to clone.
* @returns {Object} The output cloned object.
* @example
* let obj = { hello: { puny: "earthlings" }};
* let cloned = _$.clone(obj); // cloned can be operated on without changing obj
*/
export let clone = (
src = req("object", "Object to clone"),
/* These params are internal */
_visited,
_copiesVisited,
) => {
var object_create = Object.create;
if (typeof object_create !== "function") {
object_create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
if (src === null || typeof src !== "object") {
return src;
}
if (typeof src.clone == "function") {
return src.clone(true);
}
if (src instanceof Date) {
return new Date(src.getTime());
}
if (src instanceof RegExp) {
return new RegExp(src);
}
if (src.nodeType && typeof src.cloneNode == "function") {
return src.cloneNode(true);
}
if (_visited === undefined) {
_visited = [];
_copiesVisited = [];
}
var i,
len = _visited.length;
for (i = 0; i < len; i++) {
if (src === _visited[i]) {
return _copiesVisited[i];
}
}
if (Object.prototype.toString.call(src) == "[object Array]") {
var ret = src.slice();
_visited.push(src);
_copiesVisited.push(ret);
var i = ret.length;
while (i--) {
ret[i] = clone(ret[i], _visited, _copiesVisited);
}
return ret;
}
var proto = Object.getPrototypeOf
? Object.getPrototypeOf(src)
: src.__proto__;
if (!proto) {
proto = src.constructor.prototype;
}
var dest = object_create(proto);
_visited.push(src);
_copiesVisited.push(dest);
for (var key in src) {
dest[key] = clone(src[key], _visited, _copiesVisited);
}
return dest;
};
/**
* @callback listenCallback
* @param {String|Symbol} key The key being accessed
* @param {any} value The value of the key being accessed
* @returns {undefined}
*/
/**
* @memberOf object
* @function
* @param {Object} obj The object to listen to.
* @param {listenCallback} [getCallback=()=>null] The callback function to run when a value is set, with the arguments, key (the key changed) and value (the new value of the key).
* @param {listenCallback} [setCallback=()=>null] The callback function to run when a value is gotten, with the arguments, key (the key got) and value (the value of the key).
* @example
* let obj = {something: "This is part of the object", anotherThing: "This is another!"};
* obj = _$.listen(obj, (k, v) => console.log(`set ${k} to ${v}`), () => console.log("Gotten"));
* obj.something; // Logs "Gotten" to the console!
* obj.anotherThing = "Hello world!"; // Logs "Set abotherThing to Hello world!" to the console!
* @returns {Proxy} A proxy object that behaves like any other object but listens to changes.
*/
export let listen = (
obj = req("object"),
setCallback = () => null,
getCallback = () => null,
) => {
return new Proxy(obj, {
set: function (target, key, value) {
setCallback(key, value);
target[key] = value;
return target[key];
},
get: function (target, key, value) {
getCallback(key, value);
return obj[key];
},
});
};
/**
* Merges two objects into one. Note that object 2 properties will overwrite those of object 2.
* @memberOf object
* @function
* @param {Object} obj1 The 1st object to merge
* @param {Object} obj2 The 2nd object to merge.
* @returns {Object} The merged object.
* @example
* console.log(_$.merge({hello: "Hello!!"}, {world: " World", world: " Earthlings"})); // {hello: "Hello!!", world: " Earthlings"}
*/
export let merge = function MergeRecursive(
obj1 = req("object", "object 1"),
obj2 = req("object", "object 2"),
) {
for (var p in obj2) {
if (p in Object.prototype) continue;
try {
// Property in destination object set; update its value.
if (obj2[p].constructor == Object) {
obj1[p] = MergeRecursive(obj1[p], obj2[p]);
} else {
obj1[p] = obj2[p];
}
} catch (e) {
// Property in destination object not set; create it and set its value.
obj1[p] = obj2[p];
}
}
return obj1;
};
/**
* @callback mapObjKeysCallback
* @param {String} key The key
* @returns {String}
*/
/**
* Maps the keys of an object.
* @function
* @memberOf object
* @param {Object} obj The object to map.
* @param {mapObjKeysCallback} fn The function to run (passed the current key of the object) which returns the new value from that key.
* @example
* _$.mapObjectKeys({something: "A value", anotherThing: "Another value!"}, (key) => key.toUpperCase());
* //Returns {SOMETHING: "A value", ANOTHERTHING: "Another value!"}
* @returns {Object} The new Object.
*/
export let mapObjectKeys = (
obj = req("object"),
fn = req("function", "callback"),
) =>
Array.isArray(obj)
? obj.map((val) => _$.mapObjectKeys(val, fn))
: typeof obj === "object"
? Object.keys(obj).reduce((acc, current) => {
const key = fn(current);
const val = obj[current];
acc[key] =
val !== null && typeof val === "object"
? _$.mapObjectKeys(val, fn)
: val;
return acc;
}, {})
: obj;
/**
* @callback mapObjValuesCallback
* @param {any} value The value
* @returns {any}
*/
/**
* Maps an object's values.
* @memberOf object
* @function
* @param {Object} obj The object to map the values of.
* @param {mapObjValuesCallback} fn The callback function to use.
* @returns {Object} The mapped object.
* @example
* console.log(_$.mapObjectValues({ hello: "World", bijou: "is GREAT" }, val => val.toLowerCase())); // { hello: "world", bijou: "is great" }
*/
export let mapObjectValues = (
obj = req("object", "object"),
fn = req("function", "callback"),
) => {
Object.keys(obj).map(function (key, index) {
obj[key] = fn(obj[key], index);
});
return obj;
};
/**
* Converts a form to an Object.
* @function
* @memberOf object
* @param {HTMLFormElement} form The form element.
* @returns {Object} The object of form data (The keys are the "name" attributes of the form inputs and the values are the value attributes of the form data.)
* @example
* html:
* ```
* <form id="form">
* <input name"input" />
* <input name="input2" />
* </form>
* ```
* js:
* const form = document.getElementById("form");
* console.log(_$.formToObject(form)); // e.g. { input: "hello", input2: "world" }
*/
export let formToObject = (
form = req("HTMLFormElement", "the form"),
) => {
node();
return Array.from(new FormData(form)).reduce(
(acc, [key, value]) => ({
...acc,
[key]: value,
}),
);
};
/**
* Sorts an object alphabetically by its keys.
* @function
* @memberOf object
* @param {Object} obj The object to sort.
* @example
* let object = _$.sortObj({testing: "A value", anotherThing: "Another value!"});
* // The object is now {anotherThing: "Another value!", testing: "A value"}
* @returns {Object} The sorted object.
*/
export let sortObj = (obj = req("object", "object")) => {
return Object.keys(obj)
.sort()
.reduce(function (result, key) {
result[key] = obj[key];
return result;
}, {});
};
/**
* Retrieves a deeply nested value from an object given a key.
* @memberOf object
* @function
* @param {string|string[]} key - The key (if string will split by '.') or an array of keys to access the value.
* @param {object} object - The object to retrieve the value from.
* @example
* _$.deepGet("hello.world", {hello: {world: "Hello World!"}}); // "Hello World!"
* @returns {*} The retrieved value or null if the key does not exist.
*/
export let deepGet = (key, object) => {
if (typeof key === "string") {
key = key.split(".");
}
let ref = object;
for (let k of key) {
if (!ref.hasOwnProperty(k)) {
return null;
}
ref = ref[k];
}
return ref;
};
/**
* A function that sets a value at a given path in an object by creating nested objects
* along the way for any undefined keys in the path, while keeping the original object immutable.
*
* @memberOf object
* @function deepSet
* @param {string|Array<string>} path - The path to set the value at, can be either a string or an array of strings
* @param {any} value - The value to set at the given path
* @param {Object} obj - The object to set the value in
* @returns {Object} A new object with the updated value at the given path
* @example
* const obj = { a: { b: { c: 1 } } };
* const newObj = deepSet("a.b.d", 2, obj);
*
* console.log(newObj);
* // Output: { a: { b: { c: 1, d: 2 } } } }
*/
export let deepSet = (path, value, obj) => {
let clone = { ...obj };
var schema = clone;
var pList = path;
if (typeof path === "string") {
pList = path.split(".");
}
var len = pList.length;
for (var i = 0; i < len - 1; i++) {
var elem = pList[i];
if (!schema[elem]) schema[elem] = {};
schema = schema[elem];
}
schema[pList[len - 1]] = value;
return clone;
};
//#endregion Object
Source