Skip to content

Instantly share code, notes, and snippets.

@xgrommx
Forked from trxcllnt/expandRecursive.js
Created November 23, 2015 15:14
Show Gist options
  • Save xgrommx/410db958b47fda907e94 to your computer and use it in GitHub Desktop.
Save xgrommx/410db958b47fda907e94 to your computer and use it in GitHub Desktop.
Ix/Rx expandRecursive is a depth-first recursive expansion on the sequences.
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