(Javascript) itertools.groupBy, e.g. [0,0,1,1,0] -> [[0,0],[1,1],[0]] (language-specific challenge)

Specification:

Language: ECMAScript, any version[1]

Challenge: Somewhat like python's itertools.groupBy: Your function Array.group(f) should take as input an "equivalence function" f whose output defines our notion of an "equivalence key" for array elements (like a sort key). The return value of .group(f) should be an array of arrays in the same order, split whenever the value of the keyfunc parameter changes (in the === sense).

Object.defineProperties(Array.prototype, {
    group: {
        value: function(f) {/*
            CODE GOES HERE
        */}
    }
});


Examples / test cases:

&gt; ['a','c','E','G','n'].group(x =&gt; x==x.toUpperCase())
[['a','c'], ['E','G'], ['n']]

&gt; [1,1,'1'].group(x=&gt;x)
[[1,1],['1']]

&gt; [3,1,'1',10,5,5].group(x=&gt;x+2)
[[3], [1], ['1'], [10], [5,5]]

&gt; [{a:1,b:2,c:3}, {b:2,a:1,c:0}, {c:5}].group(x =&gt; JSON.stringify([x.a, x.b]))
[[{a:1,b:2,c:3},{b:2,a:1,c:0}], [{c:5}]]

&gt; [].group(x=&gt;x)
[]


Scoring criteria:

score = # of characters that replace comment above (lower is better), with easements that:


whitespace and statement-separating semicolons don't count towards total[2]
variable names and keywords each count as exactly 1 characters towards total per use[3]; this includes builtins, so Array.map.apply would be 5 'characters' (however, builtins count their full length as tiebreakers)
-5 bonus points if f being undefined has same result as the identity function x=&gt;x
you may modify the construct function(f) {...} for recursion/combinator purposes while keeping it as a function respecting rule #5, but must subtract from your score the extra characters incurred (i.e. imagine the scoring began earlier)


Disallowances:


no side-effects which affect anything other than your code (such as modifying this, window, the prototype, etc.); as a corollary, all definitions must stay local to the .group function (e.g. x=5 would be disallowed, but var x=5 is fine)


minor notes:

[1] draft specs fine if in any major browser, proprietary extensions fine if in 2+ major browsers;
[2] as long as you don't try to compile a few megabytes-long whitespace ;-)
[3] as long as reflection is not used to abuse code-in-variable-names ;-)



Errata:

Answer may do anything reasonable with regards to sparse arrays. e.g. arr=[1,1]; arr[4]=1; arr -> [1, 1, empty × 2, 1]. Possibilities include ignoring them (like .map and friends do), or treating them as undefined (like an incremental for-loop would), or anything reasonable. For example arr.group(x=&gt;x) might return [[1,1],[undefined,undefined],[1]], or [[1,1,1]], or even more dubious things like [[1,1],null,[1]] if it makes sense. You may also assume sparse arrays do not exist.

edit: #2 now applies to keywords.

You may find the following snippet useful to estimate score (the rules supercede this snippet though):

var code = `......`;
console.log('score estimate:', code.replace(/\w+/g,'X').replace(/[\s;]/g,'').replace(/X(?=X)/g,'X ').length, '-5?');
JavaScript, 47 points (53 tiebreaker)

Array.prototype.group = function(f){
    const r = []
    let k = r
    for(const i of this)
        k !== (k = f ? f(i) : i) ? r.push(a = [i]) : a.push(i)
    return r
}



Body: +52
Identity comparison: -5
Full-length built-ins: +6


Try it:



Array.prototype.group = function(f){
    const r = []
    const k = r
    for(const i of this)
        k!==(k=f?f(i):i)?r.push(a=[i]):a.push(i)
    return r
}

console.log(
    ['a','c','E','G','n'].group(x =&gt; x==x.toUpperCase()),
    [1,1,'1'].group(x=&gt;x),
    [3,1,'1',10,5,5].group(x=&gt;x+2),
    [{a:1,b:2,c:3}, {b:2,a:1,c:0}, {c:5}].group(x =&gt; JSON.stringify([x.a, x.b])),
    [].group(x=&gt;x)
)






JavaScript, 48 points (56 tiebreaker)

More traditional code golf-y solution.

Array.prototype.group = function(f,r=[],k=r){
    this.map(i=&gt;k!==(k=f?f(i):i)?r.push(a=[i]):a.push(i))
    return r
}



Signature: +9
Body: +44
Identity comparison: -5
Full-length built-ins: +8


Try it:



Array.prototype.group = function(f,r=[],k=r){
    this.map(i=&gt;k!==(k=f?f(i):i)?r.push(a=[i]):a.push(i))
    return r
}

console.log(
    ['a','c','E','G','n'].group(x =&gt; x==x.toUpperCase()),
    [1,1,'1'].group(x=&gt;x),
    [3,1,'1',10,5,5].group(x=&gt;x+2),
    [{a:1,b:2,c:3}, {b:2,a:1,c:0}, {c:5}].group(x =&gt; JSON.stringify([x.a, x.b])),
    [].group(x=&gt;x)
)