Reputation: 108520
What is the best way to convert:
['a','b','c']
to:
{
0: 'a',
1: 'b',
2: 'c'
}
Upvotes: 870
Views: 1896609
Reputation: 2085
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.
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.
// 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();
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
Reputation: 41
const arra=["java",'python','c++']
arra.__proto__={}
// { '0': 'java', '1': 'python', '2': 'c++' }
Upvotes: 0
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
Reputation: 494
The shortest answer: (using destructuring)
const obj = { ...input }
Example:
const inputArray = ["a", "b", "c"]
const outputObj = { ...inputArray }
Upvotes: 11
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
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
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
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:
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
Upvotes: 3
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
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
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
Reputation: 315
Quick and dirty #2:
var i = 0
, s = {}
, a = ['A', 'B', 'C'];
while( i < a.length ) { s[i] = a[i++] };
Upvotes: 6
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
Reputation: 81
var finalResult = ['a','b','c'].map((item , index) => ({[index] : item}));
console.log(finalResult)
Upvotes: -5
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
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
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
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
Reputation: 59
Use the javascript lodash library. There is a simple method
_.mapKeys(object, [iteratee=_.identity])
that can do the conversion.
Upvotes: 2
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
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
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
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
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
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
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
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
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
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
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