29

I have made this sandbox test:

function myLittleTest() {
  var obj, arr, armap;

  arr = [1, 2, 3, 5, 7, 11];

  obj = {};
  obj = arr;
  alert(typeof arr);
  alert(typeof obj);

  // doesn't work in IE
  armap = obj.map(function(x) {
    return x * x;
  });
  alert(typeof armap);

}
myLittleTest();

I realize I can use jQuery's function $.map for making that line of code work, but, what am I missing on javascript datatypes?

4
  • I'm not really sure what you're trying to accomplish. Do you want an array where each index represents a property from and object? Or an object where each property represents a value from the array? Commented Oct 5, 2010 at 15:32
  • You're always going to get object from typeof for arrays. Arrays are just objects with numeric properties, a few extra methods, and a magic length property. Commented Oct 5, 2010 at 15:34
  • 1
    There's no point in doing obj = {} before obj = arr. Commented Jul 3 at 15:58
  • for the typeof implied modern code can use developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Commented Jul 3 at 18:47

7 Answers 7

62

Modern Answer

If you have an iterable, you can use the spread operator.

var a = [...o];

If you have an array-like object, (like arguments, for example,) you can get a real array made from it by calling Array.from(o).

var o = {0:"a", 1:'b', length:2};
var a = Array.from(o);

a will be ["a", "b"]. This will only work right if you have a correctly set length property.


Older Answer

If you have an array-like object, (like arguments, for example,) you can get a real array made from it by calling Array.prototype.slice.call(o).

var o = {0:"a", 1:'b', length:2};
var a = Array.prototype.slice.call(o);

a will be ["a", "b"]. This will only work right if you have a correctly set length property.

Sign up to request clarification or add additional context in comments.

9 Comments

+1 - This is also a great way to make a copy of the arguments array, since arguments.slice() doesn't work on arguments... and = arguments isn't helpful. jsfiddle.net/SMayA
A shorthand for this would be [].slice.call(). This is something I learned from expressjs framework.
It's a bit wasteful though, creating a new Array everytime you need to call a method from its prototype?
Only use Array.from() for converting array-like objects to array if the end result you're looking for is a dense "packed" array. Packed arrays are slightly more efficient to read but creating them is much less efficient. The trade off is large and since chances are that is not what you need, Array.prototype.slice.call() (or almost any other Array.prototype.* method) is usually the better choice. (Using the spread operator ... to convert array-like to array is also extremely inefficient. Don't do that.)
If you read the spec, Array.from() can accept either an iterable or an array-like as its first argument. If it is an iterable (as are most array-likes) then it creates an array of length 0 and populates the array by calling iterator.next() until done. (Which when used on arrays visits empty slots.) Else if it is an array-like, it creates an array of length n (clamped to an int between 0 and 2^32-1) and copies the array-like's elements (including empty slots) to the new array. So the returned array is always dense and in the first case it's also packed. See: v8.dev/blog/elements-kinds
|
25

I think you are trying too hard...

It's easiest with jQuery (or similar library)

For this object:

var obj = {a: 1, b: 2, c: 3};

Arrays have a fixed key system so for the object above, you've got to throw away either the keys (a, b, c) or the values (1, 2, 3)

So either this:

var arr = $.map(obj, function (value, key) { return value; });

or this:

var arr = $.map(obj, function (value, key) { return key; });

1 Comment

This method will remove null "properties". In my case i need the array count and item collection to be exactly equal
7

A year ago now, but I may as well mention jQuery's makeArray function http://api.jquery.com/jQuery.makeArray/

3 Comments

var t = {f: 4, h: 5}; var u = $.makeArray(t); u will be an array with one element: object t. I doubt that's what's needed here?
Wow, Janis, you're right. What the heck is the point of that? Why not just say [t]?!?
Janis, I took some time to realize this working with the debugger… You are dam' right.
6

I'm pretty sure this isn't a type problem, it's because IE didn't have the Array.map() function until IE 9. See http://msdn.microsoft.com/en-us/library/k4h76zbx(v=VS.85).aspx for a list of supported functions. See http://msdn.microsoft.com/en-us/library/ff679976(v=VS.94).aspx for a description of the Array.map() function in IE 9.

1 Comment

msdn.microsoft.com/en-us/library/s4esdbwz(v=VS.94).aspx clearly states that the Array.map() function was introduced in IE 9.
5

Use a for loop for max browser compatibility.

In Javascript all arrays are objects, but not all object are arrays. Take a look at this Perfection Kills page which describes how to check that something is an Array.

To check for an array, you can use Object.prototype.toString.call(theObject). This will return [object Array] for an object that is an Array and [object Object] for an object that's not an Array (see example below):

            function myLittleTest() 
            {
                var obj, arr, armap, i;    

                  // arr is an object and an array
                arr = [1, 2, 3, 5, 7, 11]; 

                obj = {}; // obj is only an object... not an array

                alert (Object.prototype.toString.call(obj));
                  // ^ Output: [object Object]

                obj = arr; // obj is now an array and an object

                alert (Object.prototype.toString.call(arr));
                alert (Object.prototype.toString.call(obj));
                  // ^ Output for both: [object Array]

                // works in IE
                armap = [];
                for(i = 0; i < obj.length; ++i)
                {
                    armap.push(obj[i] * obj[i]);
                }

                alert (armap.join(", ")); 

            }
            // Changed from prueba();
            myLittleTest();

jsFiddle example

Comments

2

Among many other small utilities for manipulating objects and arrays, Underscore.js offers a toArray(obj) helper method. Documentation here: http://underscorejs.org/#toArray

It's not totally obvious from the way the documentation is written, but it works like a charm on arbitrary objects. When given an object, it iterates over the values and returns a list that contains just those values.

Comments

1

If you're converting an array-like object to an array you can use:

var array = Array.prototype.slice.call(arrayLike) // returns an array

Note that unlike Array.prototype.slice.call(), Array.from() is slow and always returns a dense array even if the given array-like is sparse. (In V8 it is a packed array.)

But since the question is how to cast a plain JavaScript object to an array, the answer is there is no built-in way other than to copy all own key/value pairs from the source object to a new array:

function cast2Array(value) {
    if (Array.isArray(value)) {
        return value;// value is already an array
    }
    if (value===null || value===undefined) {
        return [];// value is nullish
    }
    if (typeof value=="function" || Object(value)!==value) {
        return [value];// value is either a function or a primitive
    }
    // value is an object. Get property descriptors to apply to array
    var length, descriptors = Object.getOwnPropertyDescriptors(value);
    delete descriptors.length;// avoid potential RangeError
    try {
        length = Number(value.length);// throws if can't cast to number
    }
    catch(e) {
    }
    // Return new array with integer length >= 0 and descriptors applied
    return Object.defineProperties(Array(length > 0? length-length%1: 0), descriptors);
}

// Converts array-like objects to arrays
cast2Array(new String('test'));// ['t', 'e', 's', 't']
// Note: Unlike other methods the object doesn't need a length property
cast2Array({0:'a', 1:'b', 2:'c'});// ['a', 'b', 'c']
// or if value is already an array, no new array is created
cast2Array(value);// returns `value`
// Casting primitives simply returns an array containing the primitive
cast2Array('test');// ['test']
// or an empty array if the cast value is nullish
cast2Array(null);// []

If what you want is to cast a plain object to an iterable for use with modern features like for...of and the spread operator, use Object.entries() which takes a plain object and returns an array containing all of its own enumerable key/value pairs.

var object = {a:1, b:2, c:3};
Object.entries(object)// returns [['a', 1], ['b', 2], ['c', 3]]

5 Comments

Why are you still using Array.prototype.slice.call() instead of the more modern Array.from() or ellipsis notation?
Thanks for your question. Array.prototype.slice.call() is faster than Array.from() and ellipse notation only works with iterables. The question asks how to cast an object to an array. I took that literally which means we don't know if said object is even array-like much less iterable.
Another difference of Array.prototype.slice.call() besides being a lot faster is that it returns a sparse array if the given array-like object is also sparse. Array.from() always returns a packed array which might be why it has such poor performance. I would typically recommend against using Array.from() for array-like objects since adding extra overhead to get a packed array is as you said a premature optimization. Packed arrays are more efficient but the difference is negligible most of the time.
Premature optimization is the root of all evil. And isn't any array-like object an iterable?
array-like objects are not necessarily iterable and vice versa. You're probably thinking of the array-like objects generated by JavaScript such as HTML Collections, Node Lists, and the Arguments object. In that case yes they are all iterable. But technically {} is also array-like and that's not.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.