-
Notifications
You must be signed in to change notification settings - Fork 147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sorted Merge (Help Needed) #325
Comments
I think this is a bug in how generator streams interact with var i = 0;
var a = _(function (push, next) {
// This is perfectly legal. It should act the same as if you called next *after* push.
if (i === 0) {
next();
}
push(null, i++);
if (i === 5) {
push(null, _.nil);
}
else {
next();
}
});
a.pull(_.log); // => null, 1
a.each(_.log); // => nothing I'll look more into this tomorrow. |
Bug aside, your code doesn't work with async sources. You need to find a different way to determine when all streams have emitted their first value. The var s = [];
s[5] = 5;
console.log(s.length); // => 6 So if your last stream is the fastest, you'll think Also, you probably want an else here if (bufferNotFilled) {
for (var i = 0, length = srcs.length; i < length; i++) {
nextValue(i, srcs[i], push, next);
}
}
else {
// apply comparison function to find index in buffer of
// element to push
var pair = buffer.reduce(function (memo, item, idx) {
return pred(item, memo[1]) ? [idx, item] : memo;
}, [0, buffer[0]]);
// index of element in buffer allows us to know which stream to pull from
nextValue(pair[0], srcs[pair[0]], push, next);
} |
Ah right, I hadn't really considered the async case because my streams are really just a bunch of arrays I get all at once that I dump into Highland. I guess I could have another array that the sources do an |
Or a much more elegant solution given that I'm running Node 0.12 is to use a hashmap. function sortedMergeBy(pred, streams) {
var buffer = new Map();
return _(streams).collect().flatMap(function (srcs) {
function nextValue(src, push, next) {
src.pull(function (err, x) {
if (err) {
push(err);
nextValue(src, push, next);
}
else if (x === _.nil) {
// push last element in buffer
push(null, buffer.get(src));
// must be final stream
if (buffer.size === 1) {
push(null, _.nil);
}
else {
// remove stream from map of streams and
// from array of source streams
buffer.delete(src);
srcs.splice(srcs.indexOf(src), 1);
next();
}
}
else {
// buffer now contains all streams
if (buffer.size === srcs.length) {
push(null, buffer.get(src));
}
// replace old buffer key/value with new one
buffer.set(src, x);
next();
}
});
}
if (!srcs.length) {
return _([]);
}
return _(function (push, next) {
// need to buffer first element of all streams first before beginning
// comparisons
if (buffer.size < srcs.length) {
for (var i = 0, length = srcs.length; i < length; i++) {
nextValue(srcs[i], push, next);
}
}
// apply comparison function to find which stream in buffer to
// push from next
var srcToPull;
for(var pair of buffer.entries()) {
srcToPull = srcToPull == null || pred(pair[1], srcToPull[1]) ? pair : srcToPull;
}
nextValue(srcToPull[0], push, next);
});
});
}; |
Probable fix in #326. |
Brilliant! |
This might not matter to you since you don't have async streams, but the current implementation still doesn't work. You only want to run the first for loop once, but this may run multiple times if you have async streams, since you call return _(function (push, next) {
// need to buffer first element of all streams first before beginning
// comparisons
if (firstTime) {
for (var i = 0, length = srcs.length; i < length; i++) {
nextValue(srcs[i], push, next);
}
}
// apply comparison function to find which stream in buffer to
// push from next
if (buffer.length === srcs.length) {
var srcToPull;
for(var pair of buffer.entries()) {
srcToPull = srcToPull == null || pred(pair[1], srcToPull[1]) ? pair : srcToPull;
}
nextValue(srcToPull[0], push, next);
}
}); |
I see what you mean. Back to the drawing board! |
I finally got round to doing this and it looks like the fix in #326 didn't solve the issue but it does work perfectly in Highland 3.0.0! https://github.com/svozza/sortedmergeby/blob/master/sortedMergeBy.js (I swear I'm not procrastinating over my |
How do you know the fix doesn't work? I just ran your tests against Edit: typo. |
Completely missed your response here! I just realised I was running my initial tests against |
I'm having a bit of difficulty implementing a transform that merges an arbitrary number of sorted streams. I hacked around a bit with the
zipAll
implementation but for some reason only the first element of the sorted stream gets emited when I calleach
(toArray
doesn't even produce output). However, I know that the function emits the rest of the items in the correct order because if I put someconsole.log
statements in I can track the elements as they're processed. Here's what I have so far (apologies for the code dump):Here's the output of running it:
Is there anything obvious I'm doing wrong here?
The text was updated successfully, but these errors were encountered: