JavaScript equivalent of Ruby's String#scan

String.prototype.scan = function (re) {
    if (!re.global) throw "ducks";
    var s = this;
    var m, r = [];
    while (m = re.exec(s)) {
        m.shift();
        r.push(m);
    }
    return r;
};

Here's another implementation using String.replace:

String.prototype.scan = function(regex) {
    if (!regex.global) throw "regex must have 'global' flag set";
    var r = []
    this.replace(regex, function() {
        r.push(Array.prototype.slice.call(arguments, 1, -2));
    });
    return r;
}

How it works: replace will invoke the callback on every match, passing it the matched substring, the matched groups, the offset, and the full string. We only want the matched groups, so we slice out the other arguments.


ruby's scan() method will return nested array only when capture group is specified. http://ruby-doc.org/core-2.5.1/String.html#method-i-scan

a = "cruel world"
a.scan(/\w+/)        #=> ["cruel", "world"]
a.scan(/.../)        #=> ["cru", "el ", "wor"]
a.scan(/(...)/)      #=> [["cru"], ["el "], ["wor"]]
a.scan(/(..)(..)/)   #=> [["cr", "ue"], ["l ", "wo"]]

Below is modified version of melpomene's answer to return flat array if appropriate.

function scan(str, regexp) {
    if (!regexp.global) {
        throw new Error("RegExp without global (g) flag is not supported.");
    }
    var result = [];
    var m;
    while (m = regexp.exec(str)) {
        if (m.length >= 2) {
            result.push(m.slice(1));
        } else {
            result.push(m[0]);
        }
    }
    return result;
}