Skip to content

Instantly share code, notes, and snippets.

@danikaze
Created January 27, 2021 03:00
Show Gist options
  • Save danikaze/cc16e4786cdceaef49f71e27afbd5759 to your computer and use it in GitHub Desktop.
Save danikaze/cc16e4786cdceaef49f71e27afbd5759 to your computer and use it in GitHub Desktop.
/**
* 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