David Hellsing
David Hellsing

Reputation: 108520

How to convert an array into an object?

What is the best way to convert:

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

to:

{
  0: 'a',
  1: 'b',
  2: 'c'
}

Upvotes: 870

Views: 1896609

Answers (30)

Helmer Barcos
Helmer Barcos

Reputation: 2085

Well, it depends...

There are some methods available: Object.fromEntries(), reduce, forEach, Object.assign(), the spread operator ({...}) and the traditional for loop.

Object.fromEntries() performs best with few objects, while reduce(), forEach(), and for loop are more suitable for handling larger amounts of data. I will say from 1K objects you should avoid Object.fromEntries(). reduce() shows good scalability and efficiency with large datasets.

Note that Object.assign() and the spread operator ({...}) can also convert arrays to objects but have limitations with custom key mapping, making them less practical for real-world use cases.

Function performance over time

Summary

For small data sets, use Object.fromEntries(), while for larger data sets(1K+), consider using reduce(), forEach(), or for loop. Take a look to the JS snippet below if you want to see the implementation.

Benchmarks

// Benchmark function
function benchmark(fn) {
  const start = performance.now();
  fn();
  const end = performance.now();
  return (end - start).toFixed(2);
}

// 1. Using reduce
function benchmarkReduce(inputArr) {
  return benchmark(() => {
    inputArr.reduce((acc, obj) => {
      acc[obj.id] = obj;
      return acc;
    }, {});
  });
}

// 2. Using Object.fromEntries
function benchmarkFromEntries(inputArr) {
  return benchmark(() => {
    Object.fromEntries(inputArr.map(obj => [obj.id, obj]));
  });
}

// 3. Using forEach
function benchmarkForEach(inputArr) {
  return benchmark(() => {
    const obj = {};
    inputArr.forEach((item) => {
      obj[item.id] = item;
    });
  });
}

// 4. Using for loop
function benchmarkLoop(inputArr) {
  return benchmark(() => {
    const obj = {};
    for (let i = 0; i < inputArr.length; i++) {
      const item = inputArr[i];
      obj[item.id] = item;
    }
  });
}

// 5. Using Object.assign
function benchmarkObjectAssign(inputArr) {
  return benchmark(() => {
    Object.assign({}, inputArr);
  });
}

// 6. Using Spread Operator
function benchmarkSpreadOperator(inputArr) {
  return benchmark(() => {
    ({ ...inputArr
    });
  });
}

function generateMultipliers() {
  const multipliers = [];
  const step = 30;

  for (let i = 10; i < 2000; i++) {
    multipliers.push(step * (i + 1));
  }

  return multipliers;
}

function storeResults(results) {
  const jsonString = JSON.stringify(results, null, 2);
  const blob = new Blob([jsonString], {
    type: 'application/json'
  });
  const link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  link.download = 'results.json';
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}

async function executeBenchmarks() {
  const multipliers = generateMultipliers();
  const results = {};

  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    const data = await response.json();

    for (const multiplier of multipliers) {
      const sampleArray = Array.from({
        length: multiplier
      }, () => data).flat();
      // console.log(`Array Length for multiplier ${multiplier}: ${sampleArray.length}`);

      results[multiplier] = {
        reduce: benchmarkReduce(sampleArray),
        fromEntries: benchmarkFromEntries(sampleArray),
        forEach: benchmarkForEach(sampleArray),
        forLoop: benchmarkLoop(sampleArray),
        objectAssign: benchmarkObjectAssign(sampleArray),
        spreadOperator: benchmarkSpreadOperator(sampleArray),
      };
    }

    storeResults(results);

  } catch (err) {
    console.error(err);
  }
}

executeBenchmarks();

Plotting results

import json
import matplotlib.pyplot as plt

# Load the JSON data
with open('/PATH/TO/results.json') as f:
    data = json.load(f)

# Initialize lists for x values and a dictionary for y values
x_values = []
y_values = {
    "reduce": [],
    "fromEntries": [],
    "forEach": [],
    "forLoop": [],
    "objectAssign": [],
    "spreadOperator": []
}

# Fill the lists with data
for key in sorted(data.keys(), key=int):
    x_values.append(int(key))
    for func in y_values.keys():
        y_values[func].append(float(data[key][func]))  # Convert the values to float
# Create the plot
plt.figure(figsize=(10, 6))

# Plot each function with distinct, differentiable colors and thinner lines
colors = {
    "reduce": "blue",
    "fromEntries": "green",
    "forEach": "orange",
    "forLoop": "red",
    "objectAssign": "purple",
    "spreadOperator": "brown"
}

for func, color in colors.items():
    plt.plot(x_values, y_values[func], label=func, color=color, linewidth=1.5, marker='o', markersize=4)

# Customize the plot
plt.title("Function Performance over Time")
plt.xlabel("Number of Objects")
plt.ylabel("Time (ms)")
plt.legend(loc="upper left")
plt.grid(True)
plt.tight_layout()

# Show the plot
plt.show()

Upvotes: 1

Elias kibret
Elias kibret

Reputation: 41

const arra=["java",'python','c++']
arra.__proto__={}

// { '0': 'java', '1': 'python', '2': 'c++' }

Upvotes: 0

bbsimonbb
bbsimonbb

Reputation: 29022

Here's another typescript answer. This is new science, because the typing follows seamlessly. If you're able to mark your input array as const, typescript can totally infer the type of the resulting object. Magic.

This example takes a readonly input array where each element has a name property. It returns an object with the name promoted to property key, (and removed from the property value). The properties in the top level children (the former elements) are mutable, and are widened to the underlying primitive type.

const data = [
  { name: "Alice", age: 25, sex: "female" },
  { name: "Bob", age: 30 },
  { name: "Charlie", age: 35, sex: "male", hobby: "model trains" },
] as const;

interface INamed {
  readonly name: string;
}

type KeysOfUnion<T> = T extends T ? keyof T : never;

type ElementTypeFromArray<T extends readonly any[]> = {
  [K in KeysOfUnion<T[number]>]?: T[number][K];
};

type Widen<T> = T extends string
  ? string
  : T extends number
    ? number
    : T extends boolean
      ? boolean
      : T;

type Mutable<T> = {
  -readonly [K in keyof T]: Widen<T[K]>;
};

function array2Object<ElementType extends INamed>(
  array: readonly ElementType[],
): Record<
  Array<ElementType>[number]["name"],
  Mutable<Omit<ElementTypeFromArray<ElementType[]>, "name">>
> {
  const returnVal = array.reduce(
    (acc, curr) => {
      const { name, ...otherProps } = curr;
      Object.defineProperty(acc, name, {
        value: otherProps,
        enumerable: true,
        writable: true
      })
      return acc;
    },
    {} as Record<ElementType["name"], Mutable<Omit<ElementTypeFromArray<ElementType[]>, "name">>>,
  );
  return returnVal;
}

const obj = array2Object(data);

// all intellisense on obj is directly inferred from input array
obj.Alice.hobby = "knitting";
obj.Bob.age = 31; // properties are mutable, and widened to the underlying primitive type
obj.Fred = { age: 55} // error adding a top level property on the fly
obj.Alice = { age: 20 } // intellisense for 3 potential properties of Alice inside a new object literal
obj.Alice = { shoeSiez: 39 } // error when you try to add unkown properties to a person

Upvotes: 0

Bryan Enid
Bryan Enid

Reputation: 494

The shortest answer: (using destructuring)

const obj = { ...input }

Example:

const inputArray = ["a", "b", "c"]
const outputObj = { ...inputArray }

Upvotes: 11

Marinos An
Marinos An

Reputation: 10880

If your array contains 2-element arrays where first element is the key and second element is the value you can easily convert to object using reduce.

[
  ["key1","value1"], 
  ["key2", "value2"], 
  ["key3", "value3"]
]
.reduce((acc, [key, value])=>({...acc, [key]: value}), {});

Result:

{  
  key1: 'value1',   
  key2: 'value2', 
  key3: 'value3'  
}  

Upvotes: 17

j-shan huang
j-shan huang

Reputation: 130

Why No One try this? in ES6

let arr = ['a','b','c']
let {...obj} = arr
console.log(obj) //  {0: 'a', 1: 'b', 2: 'c'}
let {...obj2} = ['a','b','c']
console.log(obj2) //  {0: 'a', 1: 'b', 2: 'c'}

is Very simple way?

Upvotes: 6

giovannipds
giovannipds

Reputation: 3489

Not many people here commented of Object.fromEntries, I really enjoy it, since it's cleaner and works easily with TypeScript, without bothering too much about generic types and stuff. It also allows custom keys with map, if needed. Cons: you'll need an additional map, if you want a custom key. E.g.:

const tags = [
  { name: 'AgeGroup', value: ageGroup },
  { name: 'ApparelTypes', value: apparelTypes },
  { name: 'Brand', value: brand },
  // ...
]

const objectTags = Object.fromEntries(tags.map((t) => [t.name, t.value]))

/*
{
  AgeGroup: 'Adult',
  Apparel: 'Creeper, Jacket'
  Brand: '',
  // ...
}
*/

Upvotes: 77

Emanuel
Emanuel

Reputation: 3277

If someone is searching for a Typescript method, i wrote this:

const arrayToObject = <T extends Record<K, any>, K extends keyof any>(
  array: T[] = [],
  getKey: (item: T) => K,
) =>
  array.reduce((obj, cur) => {
    const key = getKey(cur)
    return ({...obj, [key]: cur})
  }, {} as Record<K, T>)

It will:

  1. enforce first param to be array of objects
  2. help to select the key
  3. enforce the key to be an key of all array items

Example:

// from:
const array = [
    { sid: 123, name: 'aaa', extra: 1 },
    { sid: 456, name: 'bbb' },
    { sid: 789, name: 'ccc' }
];
// to:
{
  '123': { sid: 123, name: 'aaa' },
  '456': { sid: 456, name: 'bbb' },
  '789': { sid: 789, name: 'ccc' }
}

usage:

const obj = arrayToObject(array, item => item.sid) // ok
const obj = arrayToObject(array, item => item.extra) // error

Here's a demo.

Upvotes: 3

Yilmaz
Yilmaz

Reputation: 49729

typescript solutioin:

export const toMap = (errors: ResponseError[]) => {
  const errorMap: Record<string, string> = {};
  errors.forEach(({ field, message }) => {
    errorMap[field] = message;
  });
  return errorMap;
};

export type FieldError = {
  field: string;
  message: string;
};

Upvotes: -1

Oriol
Oriol

Reputation: 288670

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" }

Upvotes: 1058

Johnathan Kanarek
Johnathan Kanarek

Reputation: 854

In case you want to use one of the properties of the iterated objects as key, for example:

// from:
const arr = [
    {
        sid: 123,
        name: 'aaa'
    },
    {
        sid: 456,
        name: 'bbb'
    },
    {
        sid: 789,
        name: 'ccc'
    }
];
// to:
{
  '123': { sid: 123, name: 'aaa' },
  '456': { sid: 456, name: 'bbb' },
  '789': { sid: 789, name: 'ccc' }
}

Use:

const result = arr.reduce((obj, cur) => ({...obj, [cur.sid]: cur}), {})

Upvotes: 60

lordkrandel
lordkrandel

Reputation: 315

Quick and dirty #2:

var i = 0
  , s = {}
  , a = ['A', 'B', 'C'];

while( i < a.length ) { s[i] = a[i++] };

Upvotes: 6

Brett Zamir
Brett Zamir

Reputation: 14365

FWIW, one another recent approach is to use the new Object.fromEntries along with Object.entries as follows:

const arr = ['a','b','c'];
arr[-2] = 'd';
arr.hello = 'e';
arr.length = 17;
const obj = Object.fromEntries(Object.entries(arr));

...which allows for avoiding storing sparse array items as undefined or null and preserves non-index (e.g., non-positive-integer/non-numeric) keys.

{ 0: "a", 1: "b", 2: "c", "-2": "d", hello: "e" }

(Same result here as with @Paul Draper's Object.assign answer.)

One may wish to add arr.length, however, as that is not included:

obj.length = arr.length;

Upvotes: 19

Gowtham Kumar B V
Gowtham Kumar B V

Reputation: 81

var finalResult = ['a','b','c'].map((item , index) => ({[index] : item}));
console.log(finalResult)

Upvotes: -5

Mark Giblin
Mark Giblin

Reputation: 1106

A simple and cheeky method of quickly converting an Array of items in to an Object

function arrayToObject( srcArray ){
    return  JSON.parse( JSON.stringify( srcArray ) );
}

Then using it like so...

var p = [0,2,3,'pork','pie',6];
obj = new arrayToObject( p );
console.log( obj[3], obj[4] )
// expecting `pork pie`

Output:

pork pie

Checking the type:

typeof obj
"object"

AND things wouldn't be complete if there wasn't a prototype method

Array.prototype.toObject =function(){
    return  JSON.parse( JSON.stringify( this ) );
}

Using like:

var q = [0,2,3,'cheese','whizz',6];
obj = q.toObject();
console.log( obj[3], obj[4] )
// expecting `cheese whizz`

Output:

cheese whizz

*NOTE that there is no naming routine, so if you want to have specific names, then you will need to continue using the existing methods below.


Older method

This allows you to generate from an array an object with keys you define in the order you want them.

Array.prototype.toObject = function(keys){
    var obj = {};
    var tmp = this; // we want the original array intact.
    if(keys.length == this.length){
        var c = this.length-1;
        while( c>=0 ){
            obj[ keys[ c ] ] = tmp[c];
            c--;
        }
    }
    return obj;
};

result = ["cheese","paint",14,8].toObject([0,"onion",4,99]);

console.log(">>> :" + result.onion); will output "paint", the function has to have arrays of equal length or you get an empty object.

Here is an updated method

Array.prototype.toObject = function(keys){
    var obj = {};
    if( keys.length == this.length)
        while( keys.length )
            obj[ keys.pop() ] = this[ keys.length ];
    return obj;
};

Upvotes: 5

Bhangun Hartani
Bhangun Hartani

Reputation: 31

Try using reflect to copy from array item to object.

var arr =['aa:23','bb:44','cc:55']
    var obj ={}
    arr.forEach(e => {
        var ee = e.split(':')
        Reflect.set(obj,ee[0],ee[1])
    });
    console.log(obj) // { aa: '23', bb: '44', cc: '55' }

Upvotes: 3

Alireza
Alireza

Reputation: 104900

More browser supported and more flexible way of doing that is using a normal loop, something like:

const arr = ['a', 'b', 'c'],
obj = {};

for (let i=0; i<arr.length; i++) {
   obj[i] = arr[i];
}

But also the modern way could be using the spread operator, like:

{...arr}

Or Object assign:

Object.assign({}, ['a', 'b', 'c']);

Both will return:

{0: "a", 1: "b", 2: "c"}

Upvotes: 6

mcmhav
mcmhav

Reputation: 1023

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

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

Upvotes: 79

jude ugwu
jude ugwu

Reputation: 59

Use the javascript lodash library. There is a simple method _.mapKeys(object, [iteratee=_.identity]) that can do the conversion.

Upvotes: 2

SridharKritha
SridharKritha

Reputation: 9701

ES5 - Solution:

Using Array prototype function 'push' and 'apply' you can populate the object with the array elements.

var arr = ['a','b','c'];
var obj = new Object();
Array.prototype.push.apply(obj, arr);
console.log(obj);    // { '0': 'a', '1': 'b', '2': 'c', length: 3 }
console.log(obj[2]); // c

Upvotes: 3

RIYAJ KHAN
RIYAJ KHAN

Reputation: 15290

Using javascript#forEach one can do this

var result = {},
    attributes = ['a', 'b','c'];

attributes.forEach(function(prop,index) {
  result[index] = prop;
});

With ECMA6:

attributes.forEach((prop,index)=>result[index] = prop);

Upvotes: 15

Daniel Snell
Daniel Snell

Reputation: 21

Simplest way to do this is the following:

const arr = ['a','b','c'];
let obj = {}

function ConvertArr(arr) { 
 if (typeof(arr) === 'array') {
 Object.assign(obj, arr);
}

This way it only runs if it is an array, however, you can run this with let global object variable or without, that's up to you, if you run without let, just do Object.assign({}, arr).

Upvotes: 2

Mahdi Bashirpour
Mahdi Bashirpour

Reputation: 18873

let i = 0;
let myArray = ["first", "second", "third", "fourth"];

const arrayToObject = (arr) =>
    Object.assign({}, ...arr.map(item => ({[i++]: item})));

console.log(arrayToObject(myArray));

Or use

myArray = ["first", "second", "third", "fourth"]
console.log({...myArray})

Upvotes: 3

murthy naika k
murthy naika k

Reputation: 579

If you're using ES6, you can use Object.assign and the spread operator

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

If you have nested array like

var arr=[[1,2,3,4]]
Object.assign(...arr.map(d => ({[d[0]]: d[1]})))

Upvotes: 10

shubhamkes
shubhamkes

Reputation: 546

Below method would convert array to object with particular given key.

    /**
     * Converts array to object
     * @param  {Array} array
     * @param  {string} key (optional)
     */
    Array.prototype.ArrayToObject = function(key) {
       const array = this;
       const obj = {};

       array.forEach((element, index) => {
           if(!key) {
              obj[index] = element;
           } else if ((element && typeof element == 'object' && element[key])) {
              obj[element[key]] = element;
           }
       });
    return obj;
    }

For Ex -

[{name: 'test'}, {name: 'test1'}].ArrayToObject('name');

would give

{test: {name: 'test'}, test1: {name: 'test1'}}

and incase key is not provided as param to the method

i.e. [{name: 'test'}, {name: 'test1'}].ArrayToObject();

would give

{0: {name: 'test'}, 1: {name: 'test1'}}

Upvotes: 1

KARTHIKEYAN.A
KARTHIKEYAN.A

Reputation: 20158

we can use Object.assign and array.reduce function to convert an Array to Object.

var arr = [{a:{b:1}},{c:{d:2}}] 
var newObj = arr.reduce((a, b) => Object.assign(a, b), {})

console.log(newObj)

Upvotes: 31

roland
roland

Reputation: 7775

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.

Upvotes: 365

Adnan Y
Adnan Y

Reputation: 3250

It's not directly relevant but I came here searching for a one liner for merging nested objects such as

const nodes = {
    node1: {
        interfaces: {if1: {}, if2: {}}
    },
    node2: {
        interfaces: {if3: {}, if4: {}}
    },
    node3: {
        interfaces: {if5: {}, if6: {}}
    },
}

The solution is to use a combination of reduce and object spread:

const allInterfaces = nodes => Object.keys(nodes).reduce((res, key) => ({...res, ...nodes[key].interfaces}), {})

Upvotes: 2

Shannon Hochkins
Shannon Hochkins

Reputation: 12186

If you can use Map or Object.assign, it's very easy.

Create an array:

const languages = ['css', 'javascript', 'php', 'html'];

The below creates an object with index as keys:

Object.assign({}, languages)

Replicate the same as above with Maps

Converts to an index based object {0 : 'css'} etc...

const indexMap = new Map(languages.map((name, i) => [i, name] ));
indexMap.get(1) // javascript

Convert to an value based object {css : 'css is great'} etc...

const valueMap = new Map(languages.map(name => [name, `${name} is great!`] ));
valueMap.get('css') // css is great

Upvotes: 4

bvmCoder
bvmCoder

Reputation: 1159

initial array and will convert into an Object with keys which will be the unique element of an array and the keys value will be how many times the perticular keys will be repeating

var jsTechs = ['angular', 'react', 'ember', 'vanilaJS', 'ember', 'angular', 'react', 'ember', 'vanilaJS', 'angular', 'react', 'ember', 'vanilaJS', 'ember', 'angular', 'react', 'ember', 'vanilaJS', 'ember', 'angular', 'react', 'ember', 'vanilaJS', 'ember', 'react', 'react', 'vanilaJS', 'react', 'vanilaJS', 'vanilaJS']

var initialValue = {
  java : 4
}

var reducerFunc = function reducerFunc (initObj, jsLib) {
  if (!initObj[jsLib]) {
    initObj[jsLib] = 1
  } else {
    initObj[jsLib] += 1
  }
  return initObj
}
var finalResult = jsTechs.reduce(reducerFunc, initialValue)
console.log(finalResult)

Upvotes: -1

Related Questions