Skip to content

Instantly share code, notes, and snippets.

@eliperelman
Forked from 140bytes/LICENSE.txt
Created June 17, 2011 15:37
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save eliperelman/1031656 to your computer and use it in GitHub Desktop.
140byt.es polyfill for Array.filter

140byt.es polyfill for Array.filter

Thanks to atk and jdalton for making the solution more robust and compliant! :)

[].filter || (Array.prototype.filter = // Use the native array filter method, if available.
function(a, //a function to test each value of the array against. Truthy values will be put into the new array and falsy values will be excluded from the new array
b, // placeholder
c, // placeholder
d, // placeholder
e // placeholder
) {
c = this; // cache the array
d = []; // array to hold the new values which match the expression
for (e in c) // for each value in the array,
~~e + '' == e && e >= 0 && // coerce the array position and if valid,
a.call(b, c[e], +e, c) && // pass the current value into the expression and if truthy,
d.push(c[e]); // add it to the new array
return d // give back the new array
})
[].filter||(Array.prototype.filter=function(a,b,c,d,e){c=this;d=[];for(e in c)~~e+''==e&&e>=0&&a.call(b,c[e],+e,c)&&d.push(c[e]);return d})
DO WHAT THE **** YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2011 Eli Perelman http://eliperelman.com
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE **** YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE **** YOU WANT TO.
{
"name": "arrayFilter140Polyfill",
"description": "A tweet-sized polyfill for Array.prototype.filter",
"keywords": [
"array",
"filter",
"polyfill",
"prototype"
]
}
<!DOCTYPE html>
<title>Array filter polyfill</title>
<div>Expected value: filter the array values outside of our range (less than 6)</div>
<script>
[].filter||(Array.prototype.filter=function(a,b,c,d,e){c=this;d=[];for(e in c)~~e+''==e&&e>=0&&a.call(b,c[e],+e,c)&&d.push(c[e]);return d});
var arr = [1,2,3,4,5,6,7,8,9,10];
// creates new array of [1,2,3,4,5] and reassigns it to arr.
arr = arr.filter(function(value) {
return value < 6;
});
</script>
@atk
Copy link

atk commented Oct 6, 2011

So

[].filter||(Array.prototype.filter||function(a,b,c,d,e){c=this;d=[];for(e in c)~~e+''==e&&e>=0&&a.call(b,c[e],e,c)&&d.push(c[e]);return d}

will do...

@atk
Copy link

atk commented Oct 6, 2011

Another stupid idea, since [].slice should normalize an object to array form (135bytes):

[].filter||(Array.prototype.filter||function(a,b,c,d,e){d=[];c=d.slice.call(this);for(e in c)a.call(b,c[e],e,c)&&d.push(c[e]);return d}

Any catches?

@jdalton
Copy link

jdalton commented Oct 6, 2011

Another stupid idea, since [].slice should normalize an object to array form (135bytes):
<snippet>
Any catches?

Yap the same old for..in gotchas with Array.prototype modifications.

@atk
Copy link

atk commented Oct 6, 2011

In all implementations I have tested, slice returns a normalized array, yet the object itself remains untouched. So only those keys that can be converted to array form will be available to for..in.

@jdalton
Copy link

jdalton commented Oct 7, 2011

So pretending this is a browser that gets this implemented:

Array.prototype.filter = function(a,b,c,d,e){d=[];c=d.slice.call(this);for(e in c)a.call(b,c[e],e,c)&&d.push(c[e]);return d};

[0, 1, 2].filter(function(value, index, array) {
  alert(index);
  return value;
});

// incorrectly alerts smth like `0` then `1` then `2` then `filter`

@atk
Copy link

atk commented Oct 7, 2011

OK, I get it. Let's stick with our working solution, then.

@eliperelman
Copy link
Author

Sorry I've been busy guys. Which one is the agreed upon solution?

@eliperelman
Copy link
Author

Updated from the comments. Thanks guys!

@jdalton
Copy link

jdalton commented Oct 17, 2011

@eliperelman

The line containing

a.call(b, c[e], e, c) &&

should be

a.call(b, c[e], +e, c) &&

So the index passed to the callback is a number and not a string.

Also devs should keep in mind that the order in which for..in iterates over properties is not spec'ed.

So for example this fallback may produce:

var a = [];
a[2] = 'c';
a[1] = 'b';
a[0] = 'a';

var b = a.filter(function(v) { return v; });
b; // ['c', 'b', 'a'];

@bga
Copy link

bga commented Oct 17, 2011

[].filter||(Array.prototype.filter=function(f,c,d){d=[];this.forEach(function(v,k,s){f.call(c,v,k,s)&&d.push(v)});return d}) 125 bytes :P

@eliperelman
Copy link
Author

@bga that defeats the purpose of this needing to be an ES5 polyfill.

@bga
Copy link

bga commented Oct 17, 2011

@eliperelman heh but you have Array#forEach polyfill too :)

@eliperelman
Copy link
Author

Agreed, but then this script wouldn't be able to operate on its own.

@tsaniel
Copy link

tsaniel commented Oct 18, 2011

I just don't understand what the difference between [].filter and Array.prototype.filter is.

@atk
Copy link

atk commented Oct 18, 2011

If you set [].filter, you set the filter method of an anonymous Array, but if you change Array.prototype.filter, you set the filter method of all Arrays. Still, [].filter is shorter, but allows to check if a (native) filter method is present.

@tsaniel
Copy link

tsaniel commented Oct 18, 2011

So why don't just use Array.prototype.filter ?

@atk
Copy link

atk commented Oct 18, 2011

If we overwrite an existing native class, we risk negative effects e.g. in Chrome. So we rather stick with this safe method.

@tsaniel
Copy link

tsaniel commented Oct 18, 2011

Isn't Array.prototype.filter || (Array.prototype.filter = ...) doing the same?

@atk
Copy link

atk commented Oct 18, 2011

Yes, but longer than [].filter||(Array.prototype.filter=...)

@tsaniel
Copy link

tsaniel commented Oct 18, 2011

That is the point.
Why is there [].filter||(Array.prototype.filter||function()...?

@atk
Copy link

atk commented Oct 18, 2011

Compared to what alternative?

@jdalton
Copy link

jdalton commented Oct 18, 2011

Why is there [].filter||(Array.prototype.filter||function()...?

It's a typo. It should be:
[].filter||(Array.prototype.filter=function()...

@eliperelman
Copy link
Author

@jdalton Are you saying this wouldn't work:

Array.prototype.filter = [].filter || (function()...

@jdalton
Copy link

jdalton commented Oct 18, 2011

@ jdalton Are you saying this wouldn't work:
Array.prototype.filter = [].filter || (function()...

Chrome has a bug where Array.prototype.filter = [].filter will cause Array#filter to become enumerable.

Also there's still a typo in the test.html snippet.

This

Array.prototype.filter=[].filter||(Array.prototype.filter=

should be this

[].filter||(Array.prototype.filter=...)

@eliperelman
Copy link
Author

Ah, I see. We don't even want to execute the polyfill if the method exists. No reassignment.

@tsaniel
Copy link

tsaniel commented Oct 19, 2011

Save 2 bytes.

[].filter||(Array.prototype.filter=function(a,b,c,d,e){d=[];for(e in c=this)~~e+''==e&&e>=0&&a.call(b,c[e],+e,c)&&d.push(c[e]);return d})

@maettig
Copy link

maettig commented Nov 24, 2011

Is there a reason why you do ~~e+''==e and e>=0? From what I know both do (almost) the same, checking if e is a number (to be more precise, the first checks for an int, the second for a double). I think it's shorter to use a plain for loop. The following version is 133 bytes and works in IE7 and IE8.

[].filter||(Array.prototype.filter=function(a,b,c,d,e){c=this;d=[];for(e=0;e<c.length;e++)a.call(b,c[e],e,c)&&d.push(c[e]);return d})

This also fixes a problem we came across in our getElementsByClassName. In IE7 and IE8 the for-in loop does not return numeric indices for elements with an ID. It returns the IDs instead for these elements. If you do (for example) document.getElementsByTagName('*') on a document where all elements contain an ID, all these elements will be skipped. Forcing e to be a number solves this problem.

@jdalton
Copy link

jdalton commented Nov 24, 2011

@maettig the ~~e+''==e and e>=0 is to ensure the key is a number and it's not negative.
Your version skips the in check for sparse arrays. You really shouldn't be using these snippets in production code.

@maettig
Copy link

maettig commented Nov 24, 2011

e>=0 is enough to check for non-negative numbers. Is it really necessary to check for decimal places? I forgot about sparse arrays. But as said, using for-in is broken in IE on NodeList's.

@jdalton
Copy link

jdalton commented Nov 24, 2011

e>=0 is enough to check for non-negative numbers. Is it really necessary to check for decimal places?

the ~~e+''==e also guards against keys like 08 too.

I forgot about sparse arrays. But as said, using for-in is broken in IE on NodeList's.

There is only so many characters allowed. As I have said before code-golf is not appropriate for production code so please don't look for it to be a robust and to-the-letter solution. As it is, the currently solution meets mark for a workable approximation of Array#filter. The important part is we have at least brought up/made aware the issues with each variation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment