Skip to content

@rwaldron /array.extensions.md
Last active

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Array.from and Array.of

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 );

// Using Array.of:
var o = (function( Ctor, args ) {
  return Ctor( ...args );
})( Array.of, args );

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

// [ "things", "that", "aren't", "currently", "an", "array" ]





// 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" ]
<div class="some classes" data-info="12"></div>
<div data-info="10"></div>
@dshaw

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.

@rwaldron
Owner

Good call, updated!

@indexzero

@rwldrn Very cool.

@polotek

+1

@jdalton

Array#slice w/ nodelists errors in IE < 9

@rwaldron
Owner

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

@guillermo

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

@rwaldron
Owner

@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

@guillermo

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.