public
Last active

Array.from and Array.of

  • Download Gist
array.extensions.md
Markdown

Array.from() & Array.of()

Original discussion began on Twitter and can be found by starting here and here. Discussion was continued on es-discuss mailing list in the thread Pure win: Array.from and Array.of

Update Nov. 3, 2011

Official strawman has been posted: http://wiki.ecmascript.org/doku.php?id=strawman:array_extras

Array.from( arg ) [ Unary ]

Converts a single argument that is an array-like object or list (eg. arguments, NodeList, DOMTokenList (used by classList), NamedNodeMap (used by attributes property)) into a new Array() and returns it;

  1. Let O be the result of calling ToObject( arg ).
  2. Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".
  3. Let len be ToUint32( lenValue ).
  4. Let A be a new array created as if by the expression new Array() where Array is the standard built-in constructor with that name.
  5. Let k be 0.
  6. Repeat, while k < len
    1. Let Pk be ToString( k ).
    2. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
    3. If kPresent is true, then
      1. Let kValue be the result of calling the [[Get]] internal method of O with argument Pk.
      2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString( k ), Property Descriptor {[[Value]]: kValue, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.

    4. Increase k by 1.
  7. return A.

Implementation (Conceptual)

Array.from = function( arg ) {

  var O = Object( arg );
  var len = O.length >>> 0;
  var A = new Array();
  var k = 0;

  while( k < len ) {

    var kValue;

    if ( k in O ) {
      kValue = O[ k ];

      A[ k ] = kValue;
    }

    k++;
  }

  return A;
};

Usage

The following use cases are based on the markup in the the attached file domjunk.html.

var divs = document.querySelectorAll("div");

Array.from( divs );
// [ <div class=​"some classes" data-info=​"12">​</div>​, <div data-info=​"10">​</div>​ ]


Array.from( divs ).forEach(function( node ) {
    console.log( node );    
});
// <div class=​"some classes" data-info=​"12">​</div>​
// <div data-info=​"10">​</div>​


Array.from( divs ).filter(function( node ) {
    return !!node.classList.length;
});
// [ <div class="some classes" data-info="12"></div> ]


Array.from( divs ).reduce(function( prev, current ) {
    return ( +prev.dataset.info ) + ( +current.dataset.info );
});
// 22


Array.from( divs[0].classList )
// ["some", "classes"]


// Now shorter then [].foo.call :)
var a = Array;


a.from( divs );
// [ <div class=​"some classes" data-info=​"12">​</div>​, <div data-info=​"10">​</div>​ ]

Array.of() [ Variable Arity ]

Array.of provides a constructor that, unlike Array, does not have the special case for new Array(42), which presets length (and hints to implementations to preallocate) but leaves holes in [0, length ).

The use-case is when you can't write a literal, because you are passing a function-that-constructs as a funarg, and the eventual caller may pass only one number arg, or several args. In that case, Array will not do the right thing in the one-number-arg case. Explanation by Brendan Eich

Implementation (Conceptual)

// Harmony/ES.next rest params
Array.of = function( ...args ) { 
  return args; 
};

// Capable today
Array.of = function() { 
  return Array.prototype.slice.call( arguments ); 
};

Usage

Array.of( 10 );
// [ 10 ]

// Versus the existing behaviour of new Array():

new Array( 10 );
// [ , , , , , , , , , , ]

// Makes sense when you can't use a literal, such this case...
// ES.next http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters
var o = (function( ArrayCtor, ...rest ) {
  return ArrayCtor( rest );
})( Array, 10 );


// Many args...
Array.of( "things", "that", "aren't", "currently", "an", "array" );

// [ "things", "that", "aren't", "currently", "an", "array" ]
array.goodies.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
// ES-Discuss Thread:
// https://mail.mozilla.org/pipermail/es-discuss/2011-July/015831.html
 
// Based on discussion originating here:
// http://twitter.com/#!/littlecalculist/status/89848378682392576
// http://twitter.com/#!/littlecalculist/status/89855977838485504
 
 
// Array.from( arrayish ) [ Unary ]
// Array.from( arrayLike ) converts any array-like object
// (eg. arguments, NodeList, DOMTokenList) into a new Array() and returns it;
 
// Implementation
 
Array.from = function( arg ) {
 
var O = Object( arg );
var len = O.length >>> 0;
var A = new Array();
var k = 0;
 
while( k < len ) {
 
var kValue;
 
if ( k in O ) {
kValue = O[ k ];
 
A[ k ] = kValue;
}
 
k++;
}
 
return A;
};
// Usage
 
var divs = document.querySelectorAll("div");
 
console.log(
Array.from( divs )
);
 
// see console
 
Array.from( divs ).forEach(function( node ) {
console.log( node );
});
 
var filtered = Array.from( divs ).filter(function( node ) {
return !!node.classList.length;
});
 
console.log( "filtered", filtered );
// filtered [<div class="some classes" data-info="12"></div>]
 
 
var reduced = Array.from( divs ).reduce(function( prev, current ) {
return ( +prev.dataset.info ) + ( +current.dataset.info );
});
 
console.log( "reduced", reduced );
// reduced 22
 
 
console.log(
Array.from( divs[0].classList )
);
 
 
var a = Array;
 
console.log(
 
// Now shorter then [].foo.call :)
a.from( divs )
);
 
 
//-------------------------------------------------------------
 
 
// Array.of() [ Variable Arity ]
// Array.of provides a constructor that, unlike Array, does not have the special case for new Array(42),
// which presets length (and hints to implementations to preallocate) but leaves holes in [0, length ).
//
// The use-case is when you can't write a literal, because you are passing a function-that-constructs
// as a funarg, and the eventual caller _may_ pass only one number arg, or several args.
// In that case, Array will not do the right thing in the one-number-arg case.
//
// See also: https://mail.mozilla.org/pipermail/es-discuss/2011-July/015841.html
 
 
// Implementation
 
// Harmony/ES.next rest params
Array.of = function( ...args ) {
return args;
};
 
// Capable today
Array.of = function() {
return Array.prototype.slice.call( arguments );
};
 
 
// Usage
 
Array.of( 10 );
// [ 10 ]
 
// Versus the existing behaviour of new Array():
 
new Array( 10 );
// [ , , , , , , , , , , ]
 
// Makes sense when you can't use a literal, such this case...
// ES.next http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters
var o = (function( ArrayCtor, ...rest ) {
return ArrayCtor( rest );
})( Array, 10 );
 
 
// Other examples:
console.log(
Array.of( "things", "that", "aren't", "currently", "an", "array" )
);
// ["things", "that", "aren't", "currently", "an", "array"]
 
console.log(
Array.of.apply( null, [ "foo", "bar", "baz" ] )
);
// [ "foo", "bar", "baz" ]
domjunk.html
HTML
1 2
<div class="some classes" data-info="12"></div>
<div data-info="10"></div>

One small suggestion: put the unary form Array.from first. It's the bigger hook and thus I think it will make it easier for those who didn't follow the conversation to grasp why this is important.

Good call, updated!

@rwldrn Very cool.

Array#slice w/ nodelists errors in IE < 9

Cool, thanks for the heads up on that. Any polyfills that expect to be "usable" will need to account for that.

For performance reasons. Instead of traversing all the array, why not change the prototype:

Array.from = function( arg ) {
  arg.__proto__ = [].__proto__;
  return arg;
};

Array.from(document.querySelectorAll('div')).forEach(function(a){console.log(a);});

If the user understand what proto means (pros/cont), I prefer this implementation. In mobile devices, the difference is notable.

http://jsperf.com/array-from-implementations

@guillermo __proto__ is non standard. The semantics I've outlined give the broadest support for arguments, NodeList, DOMTokenList and typed Arrays in all browsers. The issue is that IE6 and 7 will throw exceptions if you try to borrow Array.prototype.slice() for NodeLists

@rwldrn You are right. Too much time developing interfaces for android/ios. Sorry.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.