Created
January 27, 2021 03:00
-
-
Save danikaze/cc16e4786cdceaef49f71e27afbd5759 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Find the route to access `data` in `object`. | |
* Note that if there's more than one ocurrence of `data` in `object`, | |
* it will return an array of routes with all of them unless `onlyFirst` is | |
* `true`, in which case it will just a string with the first found route | |
* (or `undefined` if not found) | |
* | |
* @param object Object where to search for `data` | |
* @param data Data to find | |
* @param onlyFirst set to `true` to return after the first match | |
* @return Route(s) until `data` or `undefined` if not found | |
* | |
* @example | |
* const obj = { | |
* a: 1, | |
* b: { | |
* c: 2, | |
* d: 1, | |
* str: '1', | |
* }, | |
* arr: [2, 4, 6] | |
* }; | |
* findInObject(obj, 2); // ['b.c', 'arr[0]'] | |
* findInObject(obj, 1, { onlyFirst: true }); // 'a' | |
* findInObject(obj, 1); // ['a', 'b.d'] | |
* findInObject(obj, 3); // undefined | |
* findInObject(obj, 1, { comparator: (a, b) => a == b }); // ['a', 'b.str'] | |
*/ | |
function findInObject(object, data, options = {}) { | |
if (object === data) { | |
return; | |
} | |
const onlyFirst = !!options.onlyFirst; | |
return internalfindInObject( | |
object, | |
data, | |
options.comparator, | |
onlyFirst, | |
'', | |
onlyFirst ? undefined : [] | |
); | |
} | |
function internalfindInObject(object, data, comparator, onlyFirst, currentRoute, list) { | |
if (compare(comparator, object, data)) { | |
if (onlyFirst) return currentRoute; | |
list.push(currentRoute); | |
} | |
if (Array.isArray(object)) { | |
for (let i = 0; i < object.length; i++) { | |
const nextRoute = currentRoute ? `${currentRoute}[${i}]` : i; | |
const res = internalfindInObject(object[i], data, comparator, onlyFirst, nextRoute, list); | |
if (onlyFirst && res !== undefined) { | |
return res; | |
} | |
} | |
} else if (typeof object === 'object') { | |
const keys = Object.keys(object); | |
for (const key of keys) { | |
const nextRoute = currentRoute ? `${currentRoute}.${key}` : key; | |
const res = internalfindInObject(object[key], data, comparator, onlyFirst, nextRoute, list); | |
if (onlyFirst && res !== undefined) { | |
return res; | |
} | |
} | |
} | |
return onlyFirst ? undefined : list; | |
} | |
function compare(comparator, data1, data2) { | |
return typeof comparator === 'function' | |
? comparator(data1, data2) | |
: data1 === data2; | |
} | |
// | |
function test() { | |
var obj = { | |
a: 1, | |
b: { | |
c: 2, | |
d: 1, | |
str: '1', | |
}, | |
arr: [2, 4, 6] | |
}; | |
[ | |
() => findInObject(obj, 2), // ['b.c', 'arr[0]'] | |
() => findInObject(obj, 1, { onlyFirst: true }), // 'a' | |
() => findInObject(obj, 1), // ['a', 'b.d'] | |
() => findInObject(obj, 3), // undefined | |
() => findInObject(obj, 1, { comparator: (a, b) => a == b }), // ['a', 'b.d', 'b.str'] | |
].forEach((f, i) => console.log(i, f())); | |
} | |
// register it in global namespace | |
if (typeof globalThis !== 'undefined') { | |
globalThis.findInObject = findInObject; | |
console.log( | |
'Global function registered:\n' + | |
' findInObject(object, data, options)'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment