-
-
Save xgrommx/410db958b47fda907e94 to your computer and use it in GitHub Desktop.
Ix/Rx expandRecursive is a depth-first recursive expansion on the sequences.
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
var Ix = require('ix'), | |
Rx = require('rx'), | |
Enumerable = Ix.Enumerable, | |
Enumerator = Ix.Enumerator, | |
Observable = Rx.Observable; | |
Observable.permute = function(root, hasPermutation, resultSelector) { | |
return Observable | |
.returnValue({ value: root, depth: -1 }) | |
.expandRecursive(function(wrapper) { | |
var depth = wrapper.depth + 1, | |
value = wrapper.value; | |
return hasPermutation(value, depth) ? | |
Observable | |
.fromArray(Object.keys(value), Rx.Scheduler.immediate) | |
.map(function(key) { | |
return { key: key, value: value[key], depth: depth }; | |
}) : | |
Observable.empty(); | |
}) | |
.skip(1) | |
.map(function(wrapper) { | |
return resultSelector(wrapper.key, wrapper.depth); | |
}); | |
} | |
Enumerable.permute = function(root, hasPermutation, resultSelector) { | |
return Enumerable | |
.returnValue({ value: root, depth: -1 }) | |
.expandRecursive(function(wrapper) { | |
var depth = wrapper.depth + 1, | |
value = wrapper.value; | |
return hasPermutation(value, depth) ? | |
Enumerable | |
.fromArray(Object.keys(value)) | |
.map(function(key) { | |
return { key: key, value: value[key], depth: depth }; | |
}) : | |
Enumerable.empty(); | |
}) | |
.skip(1) | |
.map(function(wrapper) { | |
return resultSelector(wrapper.key, wrapper.depth); | |
}); | |
} | |
/** | |
* Expands the sequence by recursively applying a selector function depth-first. | |
* @param selector Selector function to retrieve the next sequence to expand. | |
* @returns {Enumerable} Sequence with results from the recursive expansion of the source sequence. | |
*/ | |
Enumerable.prototype.expandRecursive = function(selector) { | |
var parent = this; | |
return new Enumerable(function () { | |
var initialized = false, current, q = [], inner; | |
return Enumerator.create( | |
function () { | |
if(!initialized) { | |
initialized = true; | |
q.push(parent.getEnumerator()); | |
} | |
while (true) { | |
if (!inner) { | |
if (q.length === 0) { return false; } | |
inner = q.pop(); | |
} | |
if (inner.moveNext()) { | |
q.push(inner); | |
current = inner.getCurrent(); | |
inner = selector(current).getEnumerator(); | |
return true; | |
} else { | |
inner.dispose(); | |
inner = null; | |
} | |
} | |
}, | |
function () { return current; }, | |
function () { (initialized = false) || inner && inner.dispose(); } | |
); | |
}); | |
}; | |
/** | |
* Expands an observable sequence by recursively invoking a selector function depth-first. | |
* | |
* @param {Function} selector Selector function to invoke for each produced element, resulting in another sequence to which the selector will be invoked recursively again. | |
* @param {Scheduler} [scheduler] Scheduler on which to perform the expansion. If not provided, this defaults to the current thread scheduler. | |
* @returns {Observable} An observable sequence containing all the elements produced by the recursive expansion. | |
*/ | |
Observable.prototype.expandRecursive = function(selector, scheduler) { | |
Rx.helpers.isScheduler(scheduler) || (scheduler = Rx.Scheduler.immediate); | |
var source = this; | |
return Observable.create(function(observer) { | |
var d = new Rx.CompositeDisposable(), | |
activeCount = 0; | |
var expandObservable = function(source) { | |
d.add(scheduler.schedule(function() { | |
var m = new Rx.SingleAssignmentDisposable(); | |
d.add(m); | |
m.setDisposable(source.subscribe(function(x) { | |
observer.onNext(x); | |
var result = null; | |
try { | |
result = selector(x); | |
} catch (e) { | |
observer.onError(e); | |
} | |
activeCount++; | |
expandObservable(result); | |
}, observer.onError.bind(observer), function() { | |
d.remove(m); | |
activeCount--; | |
if(activeCount === 0) { | |
observer.onCompleted(); | |
} | |
})); | |
})); | |
} | |
expandObservable(source); | |
return d; | |
}); | |
}; | |
var inspect = require('util').inspect, | |
stackA = [], stackB = [], | |
tree = { | |
"a": { | |
"b": { | |
"c": null | |
}, | |
"d": { | |
"e": null | |
} | |
} | |
}; | |
stackA[-1] = tree; | |
stackB[-1] = tree; | |
Observable.permute( | |
tree, | |
function hasPermutation(obj, depth) { | |
return (!!obj && typeof obj === "object"); | |
}, | |
function resultSelector(key, depth) { | |
var value = stackA[depth - 1]; | |
stackA[depth] = value = value[key]; | |
return { | |
depth: depth, | |
key: key, | |
value: value | |
}; | |
}) | |
.forEach(function(wrapper) { | |
console.log("Observable(" + inspect(wrapper, true, Number.MAX_VALUE) + ")"); | |
}); | |
console.log(""); | |
Enumerable.permute( | |
tree, | |
function hasPermutation(obj, depth) { | |
return (!!obj && typeof obj === "object"); | |
}, | |
function resultSelector(key, depth) { | |
var value = stackB[depth - 1]; | |
stackB[depth] = value = value[key]; | |
return { | |
depth: depth, | |
key: key, | |
value: value | |
}; | |
}) | |
.forEach(function(wrapper) { | |
console.log("Enumerable(" + inspect(wrapper, true, Number.MAX_VALUE) + ")"); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment