Question

I want to create a javascript pipeline like powershell, bash (|) or f# (|>). Ie. something equivalent to

getstuff() | sort() | grep("foo") | take(5)

I saw a discussion about this in coffeescript forum but in the end they shelved it because everybody said that you could do the same thing with function chaining. But as far as I can see that requires getstuff returns something that has a sort method on it; the sort method must return something that has grep method on it etc. This is pretty restrictive as it requires all potential pipeline members to know about each other in advance. I know JavaScript has some pretty clever tricks in it and I am still at the 101 level - so is this doable

getstuff().sort.().grep().take()

without that constraint

Was it helpful?

Solution

is this doable

getstuff().sort.().grep().take()

without that constraint

No.


I like short answers! Can you suggest any way that something like it could be done

At a high level, you could do something similar to what jQuery does under the hood to allow chaining. Create an array-like wrapper object type which has all of the functions you want to be able to call; each successive chained call can operate on an internal stack in addition to the explicitly-passed arguments.

Not to keep beating the dead jQuery horse, but one of the best ways to understand what I'm talking about is to just start digging through the jQuery core source code and figure out how the chaining works.

OTHER TIPS

Defining an object to support the kind of function chaining you want is actually quite easy:

getStuff = ->
  sort: ->
    # set @stuff...
    this
  grep: (str) ->
    # modify @stuff...
    this
  take: (num) ->
    @stuff[num]

That's all you need to make getstuff().sort.().grep('foo').take(5) work.

You can make those calls without worrying about the return values having the appropriate methods like so:

take(5, grep("foo", sort(getstuff())));

But, that doesn't get through the problem of each function needing to be passed data that is meaningful to it. Even JavaScript isn't that slippery. You can call sort() on an image (for example,) but there's no meaningful way to generate results.

You could do something similar by returning a special object that has all required methods on it, but can be used instead of the final value. For example, you could return an Array instance that has all these methods on it.

var getstuff = function () {
    obj = Array.apply(this, arguments);
    obj.take = function (n) {
        return this[n];
    };
    obj.grep = function (regexp) {
        return getstuff.apply(this, Array.prototype.filter.apply(this, [function (item) {
            return item.toString().search(regexp) !== -1;
        }]));
    };
    obj.splice = function () {
        return getstuff.apply(this, Array.prototype.splice.apply(this, arguments));
    }
    return obj;
}

// shows [-8, 1]
console.log(getstuff(3, 1, 2, 'b', -8).sort().grep(/\d+/).splice(0, 2));
// shows 3
var stuff = getstuff(3, 1, 2, 'b', -8).grep(/\d+/);
console.log(stuff.sort()[stuff.length]);

Note that the above is not a particularly fast implementation, but it returns arrays with special methods by still keeping the global Allay's prototype clean, so it won't interfere with other code.

You could make it faster by defining these special methods on the Array.prototype, but you should be careful with that...

Or, if your browser supports subclassing Array, then all you need is a supclass and a handy constructor, getstuff().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top