Q:

Underscore.js .filter() and .any()

I have an array of event objects called events. Each event has markets, an array containing market objects. Inside here there is another array called outcomes, containing outcome objects.
In this question, I asked for a [Underscore.js] way to find all of the events which have markets which have outcomes which have a property named test. The answer was:
// filter where condition is true
_.filter(events, function(evt) {

    // return true where condition is true for any market
    return _.any(evt.markets, function(mkt) {

        // return true where any outcome has a "test" property defined
        return _.any(mkt.outcomes, function(outc) {
            return outc.test !== "undefined" && outc.test !== "bar";
        });
    });
});

This works great, but I'm wondering how I would alter it if I wanted to filter the outcomes for each market, so that market.outcomes only stored outcomes that were equal to bar. Currently, this is just giving me markets which have outcomes which have some set test properties. I want to strip out the ones that do not.

A:

Make it a simple loop, using the splice method for the array removals:
var events = [{markets:[{outcomes:[{test:x},...]},...]},...];
for (var i=0; i<events.length; i++) {
    var mrks = events[i].markets;
    for (var j=0; j<mrks.length; j++) {
        var otcs = mrks[j].outcomes;
        for (var k=0; k<otcs.length; k++) {
            if (! ("test" in otcs[k]))
                 otcs.splice(k--, 1); // remove the outcome from the array
        }
        if (otcs.length == 0)
            mrks.splice(j--, 1); // remove the market from the array
    }
    if (mrks.length == 0)
        events.splice(i--, 1); // remove the event from the array
}

This code will remove all outcomes that have no test property, all empty markets and all empty events from the events array.
An Underscore version might look like that:
events = _.filter(events, function(evt) {
    evt.markets = _.filter(evt.markets, function(mkt) {
        mkt.outcomes = _.filter(mkt.outcomes, function(otc) {
            return "test" in otc;
        });
        return mkt.outcomes.length > 0;
    });
    return evt.markets.length > 0;
});

