Skip to content

Instantly share code, notes, and snippets.

@rwaldron
Last active May 4, 2022 13:46
Show Gist options
  • Save rwaldron/1074126 to your computer and use it in GitHub Desktop.
Save rwaldron/1074126 to your computer and use it in GitHub Desktop.
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>
@rwaldron
Copy link
Author

@dshaw
Copy link

dshaw commented Jul 10, 2011

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
Copy link
Author

Good call, updated!

@indexzero
Copy link

@rwldrn Very cool.

@polotek
Copy link

polotek commented Jul 10, 2011

+1

@jdalton
Copy link

jdalton commented Oct 28, 2011

Array#slice w/ nodelists errors in IE < 9

@rwaldron
Copy link
Author

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

@guillermo
Copy link

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
Copy link
Author

@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
Copy link

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

@rousan
Copy link

rousan commented Jun 10, 2017

@guillermo

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

function A() {
    this.length = 2;
    this[0] = "elem1";
    this[1] = "elem2";
}

A.prototype.printLength = function () {
    console.log(this.length);
};

var a = new A();
console.log(a.printLength());
Array.from(a);
console.log(a.printLength()); // TypeError: a.printLength is not a function

[[Prototype]] of arg is changed, it is not a good idea.

@jaydevpatidar
Copy link

jaydevpatidar commented May 4, 2022

how do i convert a object type to array type
have any function to convert object to array

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