Muthu Kumar
7 years ago
1 changed files with 77 additions and 40 deletions
@ -1,67 +1,104 @@ |
|||||
"use strict"; |
"use strict"; |
||||
|
|
||||
const isObject = obj => obj && typeof obj === 'object' && obj.constructor === Object; |
/** |
||||
|
* Returns true if x is an object, false otherwise. |
||||
|
* @param {any} x |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
const isObject = x => x && |
||||
|
typeof x === 'object' && |
||||
|
x.constructor === Object; |
||||
|
|
||||
const makeCircular = (obj, path) => { |
/* A well known Symbol. */ |
||||
const start = (o, p, i = 1) => { |
const $SELF = typeof Symbol !== 'undefined' ? |
||||
if (i === path.length) return o[p[0]] = obj; |
Symbol('SELF') : |
||||
if (p.length > 0) { |
'[~~//-- SELF --//~~]'; |
||||
o[p[0]] = {}; |
|
||||
return start(o[p[0]], p.slice(1), i + 1); |
|
||||
} else return obj; |
|
||||
} |
|
||||
return start(obj, path); |
|
||||
}; |
|
||||
|
|
||||
const findPath = (obj, query) => { |
/** |
||||
return Object.keys(obj).reduce((acc, key) => { |
* Replaces values that match the query parameter |
||||
|
* with a reference to the parent parameter. |
||||
|
* @param {Object} object Object to make cyclic. |
||||
|
* @param {any} query Query to match against. |
||||
|
* @returns {Object} |
||||
|
*/ |
||||
|
const makeCyclic = (object, query) => { |
||||
|
const start = obj => Object.keys(obj).reduce((acc, key) => { |
||||
const value = obj[key]; |
const value = obj[key]; |
||||
if (value === query) return [...acc, key]; |
if (value === query) { |
||||
if (isObject(value)) return [...acc, ...findPath(value)]; |
obj[key] = object; |
||||
|
return [...acc, key] |
||||
|
}; |
||||
|
if (isObject(value)) return [...acc, ...start(value, query)]; |
||||
else return acc; |
else return acc; |
||||
}, []); |
}, []); |
||||
|
return start(object); |
||||
}; |
}; |
||||
|
|
||||
// Resolves array
|
/** |
||||
|
* Promise.map polyfill. |
||||
|
* @param {Array.<any>} arr Array of Promises. |
||||
|
* @param {Function} functor Function to call resolved values. |
||||
|
*/ |
||||
const PromiseMap = (arr, functor) => |
const PromiseMap = (arr, functor) => |
||||
Promise.all(arr.map(x => Promise.resolve(x).then(functor))); |
Promise.all(arr.map(x => Promise.resolve(x).then(functor))); |
||||
|
|
||||
// Resolves objects
|
/** |
||||
|
* Resolve a flat object's promises. |
||||
|
* @param {Object} |
||||
|
* @returns {Object} |
||||
|
*/ |
||||
const ResolveObject = obj => |
const ResolveObject = obj => |
||||
Promise.all( |
Promise.all( |
||||
Object.keys(obj).map(key => |
Object |
||||
Promise.resolve(obj[key]).then(val => obj[key] = val)) |
.keys(obj) |
||||
|
.map(key => |
||||
|
Promise |
||||
|
.resolve(obj[key]) |
||||
|
.then(val => obj[key] = val)) |
||||
) |
) |
||||
.then(_ => obj); |
.then(_ => obj); |
||||
|
|
||||
// Resolves recrusive deep objects
|
/** |
||||
const PromiseObject = parent => { |
* Recursively resolves deep objects with nested promises. |
||||
const ResolveDeepObject = object => |
* @param {Object} object Object or value to resolve. |
||||
|
* @returns {Object} Resolved object. |
||||
|
*/ |
||||
|
const PromiseObject = object => { |
||||
|
let shouldReplaceSelf = false; |
||||
|
const ResolveDeepObject = obj => |
||||
Promise |
Promise |
||||
.resolve(object) |
.resolve(obj) |
||||
.then(obj => { |
.then(resolvedObject => { |
||||
if (Array.isArray(obj)) { |
if (Array.isArray(resolvedObject)) { |
||||
return PromiseMap(obj, obj => ResolveDeepObject(obj)); |
// Promise and map every item to recursively deep resolve.
|
||||
} else if (isObject(obj)) { |
return PromiseMap(resolvedObject, obj => ResolveDeepObject(obj)); |
||||
|
} else if (isObject(resolvedObject)) { |
||||
return ResolveObject( |
return ResolveObject( |
||||
Object |
Object |
||||
.keys(obj) |
.keys(resolvedObject) |
||||
.reduce((acc, key) => |
.reduce((acc, key) => { |
||||
(obj[key] === parent) ? { |
if (resolvedObject[key] === object) { |
||||
|
shouldReplaceSelf = true; |
||||
|
return { |
||||
...acc, |
...acc, |
||||
[key]: '[[ SELF ]]', |
[key]: $SELF, // Replace with resolved object.
|
||||
} : { |
} |
||||
|
} |
||||
|
return { |
||||
...acc, |
...acc, |
||||
[key]: ResolveDeepObject(obj[key]), |
[key]: ResolveDeepObject(resolvedObject[key]), |
||||
|
} |
||||
}, {})); |
}, {})); |
||||
|
|
||||
} |
} |
||||
return obj; |
return resolvedObject; |
||||
}); |
}); |
||||
return ResolveDeepObject(parent) |
return ResolveDeepObject(object) |
||||
.then(obj => { |
.then(obj => { |
||||
const path = findPath(obj, '[[ SELF ]]'); |
// Replace $SELF with reference to obj
|
||||
return path.length > 0 ? makeCircular(obj, path) : obj; |
if(shouldReplaceSelf) makeCyclic(obj, $SELF); |
||||
}) |
return obj; |
||||
|
}); |
||||
}; |
}; |
||||
|
|
||||
module.exports = PromiseObject; |
module.exports = PromiseObject; |
Loading…
Reference in new issue