Convert Array to Object

1,246,085

Solution 1

ECMAScript 6 introduces the easily polyfillable Object.assign:

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

Object.assign({}, ['a','b','c']); // {0:"a", 1:"b", 2:"c"}

The own length property of the array is not copied because it isn't enumerable.

Also, you can use ES8 spread syntax on objects to achieve the same result:

{ ...['a', 'b', 'c'] }

For custom keys you can use reduce:

['a', 'b', 'c'].reduce((a, v) => ({ ...a, [v]: v}), {}) 
// { a: "a", b: "b", c: "c" }

Solution 2

With a function like this:

function toObject(arr) {
  var rv = {};
  for (var i = 0; i < arr.length; ++i)
    rv[i] = arr[i];
  return rv;
}

Your array already is more-or-less just an object, but arrays do have some "interesting" and special behavior with respect to integer-named properties. The above will give you a plain object.

edit oh also you might want to account for "holes" in the array:

function toObject(arr) {
  var rv = {};
  for (var i = 0; i < arr.length; ++i)
    if (arr[i] !== undefined) rv[i] = arr[i];
  return rv;
}

In modern JavaScript runtimes, you can use the .reduce() method:

var obj = arr.reduce(function(acc, cur, i) {
  acc[i] = cur;
  return acc;
}, {});

That one also avoids "holes" in the array, because that's how .reduce() works.

Solution 3

You could use an accumulator aka reduce.

['a','b','c'].reduce(function(result, item, index, array) {
  result[index] = item; //a, b, c
  return result;
}, {}) //watch out the empty {}, which is passed as "result"

Pass an empty object {} as a starting point; then "augment" that object incrementally. At the end of the iterations, result will be {"0": "a", "1": "b", "2": "c"}

If your array is a set of key-value pair objects:

[{ a: 1},{ b: 2},{ c: 3}].reduce(function(result, item) {
  var key = Object.keys(item)[0]; //first property: a, b, c
  result[key] = item[key];
  return result;
}, {});

will produce: {a: 1, b: 2, c: 3}

For the sake of completeness, reduceRight allows you to iterate over your array in reverse order:

[{ a: 1},{ b: 2},{ c: 3}].reduceRight(/* same implementation as above */)

will produce: {c:3, b:2, a:1}

Your accumulator can be of any type for you specific purpose. For example in order to swap the key and value of your object in an array, pass []:

[{ a: 1},{ b: 2},{ c: 3}].reduce(function(result, item, index) {
  var key = Object.keys(item)[0]; //first property: a, b, c
  var value = item[key];
  var obj = {};
  obj[value] = key;
  result.push(obj);
  return result;
}, []); //an empty array

will produce: [{1: "a"}, {2: "b"}, {3: "c"}]

Unlike map, reduce may not be used as a 1-1 mapping. You have full control over the items you want to include or exclude. Therefore reduce allows you to achieve what filter does, which makes reduce very versatile:

[{ a: 1},{ b: 2},{ c: 3}].reduce(function(result, item, index) {
  if(index !== 0) { //skip the first item
    result.push(item);
  }
  return result;
}, []); //an empty array

will produce: [{2: "b"}, {3: "c"}]

Caution: reduce and Object.key are part of ECMA 5th edition; you should provide a polyfill for browsers that don't support them (notably IE8).

See a default implementation by Mozilla.

Solution 4

If you're using jquery:

$.extend({}, ['a', 'b', 'c']);

Solution 5

For completeness, ECMAScript 2015(ES6) spreading. Will require either a transpiler(Babel) or an environment running at least ES6.

console.log(
   { ...['a', 'b', 'c'] }
)

Share:
1,246,085
David Hellsing
Author by

David Hellsing

Pushing binaries at Aino.

Updated on July 27, 2022

Comments

  • David Hellsing
    David Hellsing almost 2 years

    What is the best way to convert:

    ['a','b','c']
    

    to:

    {
      0: 'a',
      1: 'b',
      2: 'c'
    }
    
  • Phrogz
    Phrogz over 13 years
    I think you forgot to test that; it produces {0:"c", 1:"b", 2:"a"}. You either want unshift instead of pop or (better) start with i=arr.length-1 and decrement instead.
  • Mic
    Mic over 13 years
    yeah.. just changed it, but then it become less interesting :/
  • Dave Dopson
    Dave Dopson about 12 years
    Note that your "typeOf" function is notoriously dangerous - some objects just happen to have a "length" property. I'd recommend using the methods in underscore.js, like "_.isArray(obj)".
  • Dave Dopson
    Dave Dopson almost 12 years
    you downvote my response, but no comment? My answer is correct and tested. What is the objection?
  • TrySpace
    TrySpace almost 12 years
    Weird, if I give in an Array variable it puts every character in a separate object: 0: "a", 1: ",", 2: "b"... So it ignores the quotes, but includes the commas...
  • Johan
    Johan almost 12 years
    just check for obj instanceof Array instead of checking for length
  • James Daly
    James Daly over 11 years
    thought this was great just added a few things for my own enjoyment returns an object you can't reference outside function so just showed example of that and adding something to the object property names x = ['a','b','c'] var b = {} function toObject(arr) { var rv = {}; for (var i = 0; i < arr.length; ++i) if (arr[i] !== undefined) { rv['key_' + i] = arr[i]; } b = rv return b; } toObject(x) b.key_0
  • David Hellsing
    David Hellsing over 11 years
    There are a couple of problems with this approach, 1; you are extending the native prototype. 2; you are using for in in an array wich will also loop through static properties of the array. 3; why are you excluding functions?
  • David Hellsing
    David Hellsing over 11 years
    This question has no underscore tag, and you are also assuming node.js or a require library.
  • Dave Dopson
    Dave Dopson over 11 years
    underscore is pretty common on both client and server. It's assumed for Backbone.js and is probably THE most common utility library. That being said, I included the require line to make it clear I was using a library. There's no 1-liner for describing "add underscore.js to your page", so some translation is required for a browser environment
  • David Hellsing
    David Hellsing over 11 years
    Still, if you’re using undserscore, a simple obj=_.extend({},a); would do the job. Also, if you are iterating through arrays I’d say _.each would be more appropriate than _.map. All in all, this is not a good answer on several levels.
  • Dave Dopson
    Dave Dopson over 11 years
    Your suggestion to use _.extend is a good one. I didn't realize that would work on an array. Normally I prefer 'map' to 'each' because perf micro-optimization aside, 'map' can accomplish everything that 'each' accomplishes, reducing by 1 the number of methods I need to think about.... that's one of my few complaints about underscore, having too many methods where a smaller canonical set would suffice. JS1.6 seems to agree with me, defining a built in 'map', but no 'each' method.
  • Conner Ruhl
    Conner Ruhl over 11 years
    Why are you using comma notation?
  • Aaron Miler
    Aaron Miler about 11 years
    Using underscore and Backbone and this solved my problem perfectly. Thanks.
  • m93a
    m93a about 11 years
    What about var arr = []; arr['foo'] = "bar"? Can this be converted to object?
  • Pointy
    Pointy about 11 years
    @m93a it already is an object. In JavaScript, there's really no point creating an Array instance ([]) if you're not going to use numeric property keys and the "length" property.
  • m93a
    m93a about 11 years
    @Pointy Nope, it's not an usual object - try converting to JSON, it treats really werid.
  • m93a
    m93a about 11 years
    What's coffeescript? We are talking about JS! :D
  • Pointy
    Pointy about 11 years
    @m93a All array instances are objects. Properties with string (non-numeric) names like "foo" are merely properties of that object, and as such they're just like properties of any other object. The properties with numeric names (like "12" or "1002") are special in that the runtime system automatically adjusts the "length" property accordingly when values are assigned. Also, .push(), .pop(), .slice(), etc. all only work on numeric properties, and not properties like "foo".
  • David
    David about 11 years
    It's a little language that compiles into JavaScript :)
  • m93a
    m93a about 11 years
    Hmph, it uses strange notation. I like plain JS more.
  • m93a
    m93a about 11 years
    @Pointy Try to run this in your JS console: var a = ["x", "y"]; a["foo"] = "z"; b = {1:"x",2:"y","foo":"z"}; console.log(JSON.stringify(a)); console.log(JSON.stringify(b));.
  • Pointy
    Pointy about 11 years
    @m93a the JSON.stringify code can tell the difference between a real Array instance and an object with numeric keys. When it sees a real array, all that it serializes is the "real" list of properties that comprise the numerically-indexed array. I have never noticed that behavior, but it's yet another good reason not to use an Array when you really just want an Object.
  • BingeBoy
    BingeBoy almost 11 years
    Personal preference to have the comma leading on the next line. I find this notation easier to read.
  • ivkremer
    ivkremer over 10 years
    @Max I think there is no such behavior. The returned object is {0: 'a', 1: 'b', 2: 'c'} what is an expected result.
  • Brett Zamir
    Brett Zamir over 10 years
    This destroys both the keys array content and, despite the internal comment, also the values array (its contents). JavaScript works differently than PHP with JavaScript automatically acting by reference on the properties of an object/array.
  • Mark Giblin
    Mark Giblin over 10 years
    No it doesn't, the routine makes copies, the original is not touched.
  • Brett Zamir
    Brett Zamir over 10 years
    Yes, the original is touched. See jsfiddle.net/bRTv7 . The array lengths end up both being 0.
  • Mark Giblin
    Mark Giblin over 10 years
    Ok, the edited version I just changed it with does not destroy the arrays, find that strange that it should have done that.
  • Brett Zamir
    Brett Zamir over 10 years
    var tmp = this; had just made a reference, not a copy, as with the keys array you had passed into the function. As mentioned, JavaScript works differently than PHP. I also fixed your current function's while loop condition to be >=0 instead of >0 because otherwise you would avoid including the first item in the array (at the 0 index).
  • Brett Zamir
    Brett Zamir over 10 years
    See jsfiddle.net/WpM3h for your latest version before my modification: jsfiddle.net/WpM3h
  • 2540625
    2540625 over 9 years
    @Pointy, what are your one- and two-letter variables supposed to indicate?
  • Pointy
    Pointy over 9 years
    @janaspage well "i" is just a numeric index into the numbered properties of the array, and "rv" is the return value of that function.
  • Davi Lima
    Davi Lima over 9 years
    Is there a way to use $.extend while also informing the keys in a non-numerical manner, ie: making calcultations on the array items?
  • Charlie Martin
    Charlie Martin over 9 years
    The "you must answer without mentioning underscore or lo-dash" people are so annoying. Comparable to saying that you must answer C++ questions without mentioning the standard libraries. If underscore or lo-dash is not an option, the OP should mention that.
  • XåpplI'-I0llwlg'I  -
    XåpplI'-I0llwlg'I - about 9 years
    @David You could do the entire for loop on 1 line to make the whole thing only 3 lines. :) Example: obj[i] = v for v,i in arr when v?
  • Qix - MONICA WAS MISTREATED
    Qix - MONICA WAS MISTREATED over 8 years
    Could even do l = arr.length, and then while (l && (obj[--l] = arr.pop())){} (I realize this is old, but why not simplify it even further).
  • Mic
    Mic over 8 years
    @Qix, Nice shortening
  • Oriol
    Oriol about 8 years
    (1) According to MDN, changing the [[Prototype]] of an object is a very slow operation. (2) This does not remove the own length property. (3) The object is still an array, Array.isArray(arr) === true. (4) Special array behaviors are not removed, e.g. arr.length = 0 removes all indices. (5) Therefore, I think Object.assign is much better.
  • Oriol
    Oriol about 8 years
    The loop will stop if the array contains a falsy value. You should check i < a.length instead of a[i].
  • Benjamin Gruenbaum
    Benjamin Gruenbaum almost 8 years
    @Oriol mdn is wrong, at least in V8 (but also other engines) this is a pretty fast operation in this particular case - since objects have a numerical store anyway this is basically changing two pointers.
  • huygn
    huygn over 7 years
    cleaner way to avoid param reassign const obj = arr.reduce((obj, cur, i) => { return { ...obj, [i]: cur }; }, {});
  • Hanmaslah
    Hanmaslah over 7 years
    Works really well. Thanks
  • morewry
    morewry about 7 years
    @CharlieMartin that analogy is completely flawed, because lodash, underscore, and jQuery are not "standard libraries" and, in a great number of JavaScript use cases, adding libraries directly impacts end users, which one should not do for trivial reasons.
  • Charlie Martin
    Charlie Martin about 7 years
    @morewry I was trying to suggest that they should be in the standard libraries in javascript. Not that they are. _.extend was added to the standard libraries in ECMA 6 as Object.assign anyway
  • Shannon Hochkins
    Shannon Hochkins about 7 years
    Not only are your variable names different, but your example will not work, creating a map from the constructor initially requires 2d key-value Array. new Map([['key1', 'value1'], ['key2', 'value2']]);
  • Wanjia
    Wanjia almost 7 years
    This should be higher up, if you have ['a' = '1', 'b' = '2', 'c' = '3'] and want it like {a: 1, b: 2, c: 3} this works perfect.
  • Watchmaker
    Watchmaker over 6 years
    If you had an array of objects and you would like to transform that array in a n object you could do: const optionPriceArray = [ {option: 'economy', price:200}, {option: 'tourist', price:100} ]; and then: const newObj = optionPriceArray.reduce(function(acc, cur) { acc[cur.option] = cur.price; return acc; }, {}); which outputs: //newObj = {'economy': 200, 'tourist': 100}
  • VivekN
    VivekN about 6 years
    @huygn will your approach be not more expensive in terms of number of lines that it has to process.Since everytime instead of using the same object, you are creating a new one by spreading the params which to es5 would be a for loop iterating over the properties.It seems that this might just make the function slow.What do you think?
  • Aluan Haddad
    Aluan Haddad about 6 years
    Actually this isn't natively available in ES2015 either. It is very elegant however.
  • Marc Scheib
    Marc Scheib about 6 years
    Arrow + destructuring syntax w/o explicit return: const obj = arr.reduce((obj, cur, i) => ({ ...obj, [i]: cur }), {});
  • miro
    miro almost 6 years
    As @VivekN said, is it necessary to create new object every time? What about arr.reduce((obj, cur, i) => (obj[i]=cur,obj), {}); ?
  • Pointy
    Pointy almost 6 years
    @eugene_sunic indeed, but that approach was not available when I answered this in 2010 :)
  • Brett Zamir
    Brett Zamir over 5 years
    This also can be used for the fact that it is apparently unique among the other quick solutions, of preserving any non-index ("own") properties on the array (i.e., non-positive-integer properties)...
  • Brett Zamir
    Brett Zamir over 5 years
    But hmm, Array.isArray is returning true for the "object" here even though instanceof Array does not...
  • Benjamin Gruenbaum
    Benjamin Gruenbaum over 5 years
    @BrettZamir that sounds like a bug :D
  • Prashant Pimpale
    Prashant Pimpale over 5 years
    What if I want to convert to { name: a, div :b, salary:c}
  • Dan Dascalescu
    Dan Dascalescu about 5 years
    What did you intend with [/docs]? It would be more useful to make the code snippets executable.
  • dhruvpatel
    dhruvpatel almost 5 years
    Helpful with maintaining a redux store when you are using a map of objects and you need to drop one of the keys
  • HappyHands31
    HappyHands31 almost 5 years
    Just want to point out - if you already have an array of sorted properties from the original object, using the spread operator is what will turn that array directly into a new object: { ...[sortedArray]}
  • Jeff Fairley
    Jeff Fairley over 4 years
    Also, Object.fromEntries(Object.entries(nodes).map(([key, value] => [key, value.interfaces]))
  • romellem
    romellem over 4 years
    As @miro points out, creating a new object every time is not necessary, and in fact much slower than manipulating the initial object that was created. Running a JSPerf test on an array of 1000 elements, creating a new object every time is 1,000 times slower than mutating the existing object. jsperf.com/…
  • Mark Giblin
    Mark Giblin over 4 years
    Updated method that should be supported in older browsers as JSON has been around longer than ECMA added new code features
  • Mike K
    Mike K about 4 years
    This is what I was looking for. I didn't need the indeces, I needed to transform the array into retained key-value pairs.
  • Faisal Mehmood Awan
    Faisal Mehmood Awan about 4 years
    super awsome and short test
  • Menai Ala Eddine - Aladdin
    Menai Ala Eddine - Aladdin about 4 years
    Oriol, is there a way to set a fixed key instead of 0 & 1?
  • its4zahoor
    its4zahoor almost 4 years
    the returns an array of objects. the question was about a single object with index as a key and value as the value.
  • Thomas Beauvais
    Thomas Beauvais over 3 years
    This is usually not what you want. Usually you want to use a property in the array element as a key. Thus, the reduce is what you want.
  • PEZO
    PEZO about 3 years
    Is it faster then the other solutions?
  • Admin
    Admin about 3 years
    why mvc does not allow to us to post array. Why we must convert array to object?
  • Kanish
    Kanish about 3 years
    how to get result - { "a": "a", "b":"b", "c":"c" } using spread?
  • panzerpower
    panzerpower about 3 years
  • Gel
    Gel almost 3 years
    what if i want the key not as 0,1,2 but the same as the value? like: {a: 'a'} How do we do that ?
  • David Hellsing
    David Hellsing over 2 years
    @Gel Use reduce like: arr.reduce((a, v) => ({ ...a, [v]: v}), {})
  • Balazs Zsoldos
    Balazs Zsoldos over 2 years
    I am wondering how fast this reduce thing is. In each iteration the object is cloned again and again with spread operator?
  • Lasf
    Lasf about 2 years
    This is nice and easily modified to meet other reqs
  • alextrastero
    alextrastero almost 2 years
    This is just what I was looking for, I can do: {...arr} and done
  • Cristik
    Cristik almost 2 years
    This is the same solution as provided in the top answer
  • Cristik
    Cristik almost 2 years
    The same solution was already provided in the second top answer.
  • Cristik
    Cristik almost 2 years
    The same solution was already provided in the second top answer.
  • Meitham
    Meitham almost 2 years
    @Cristik it does now, but it didn’t at the time. It’s one annoying trend now for top answers to copy snippets from bottom answers without providing credit.
  • Cristik
    Cristik almost 2 years
    I looked at the revision history before posting the comment, and it showed the edit that introduced the reduce() was added on Mar 12, 2014, which was before your answer was posted.