Flatten array with objects into 1 object - javascript

Given input:
[{ a: 1 }, { b: 2 }, { c: 3 }]
How to return:
{ a: 1, b: 2, c: 3 }
For arrays it's not a problem with lodash but here we have array of objects.

Use Object.assign:
let merged = Object.assign(...arr); // ES6 (2015) syntax
var merged = Object.assign.apply(Object, arr); // ES5 syntax
Note that Object.assign is not yet implemented in many environment and you might need to polyfill it (either with core-js, another polyfill or using the polyfill on MDN).
You mentioned lodash, so it's worth pointing out it comes with a _.assign function for this purpose that does the same thing:
var merged = _.assign.apply(_, [{ a: 1 }, { b: 2 }, { c: 3 }]);
But I really recommend the new standard library way.

With lodash, you can use merge():
var arr = [ { a: 1 }, { b: 2 }, { c: 3 } ];
_.merge.apply(null, [{}].concat(arr));
// → { a: 1, b: 2, c: 3 }
If you're doing this in several places, you can make merge() a little more elegant by using partial() and spread():
var merge = _.spread(_.partial(_.merge, {}));
merge(arr);
// → { a: 1, b: 2, c: 3 }

Here is a version not using ES6 methods...
var arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
var obj = {};
for(var i = 0; i < arr.length; i++) {
var o = arr[i];
for(var key in o) {
if(typeof o[key] != 'function'){
obj[key] = o[key];
}
}
}
console.log(obj);
fiddle: http://jsfiddle.net/yaw3wbb8/

You can use underscore.extend function like that:
var _ = require('underscore');
var a = [{ a: 1 }, { b: 2 }, { c: 3 }];
var result = _.extend.apply(null, a);
console.log(result); // { a: 1, b: 2, c: 3 }
console.log(a); // [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]
And to prevent modifying original array you should use
var _ = require('underscore');
var a = [{ a: 1 }, { b: 2 }, { c: 3 }];
var result = _.extend.apply(null, [{}].concat(a));
console.log(result); // { a: 1, b: 2, c: 3 }
console.log(a); // [ { a: 1 }, { b: 2 }, { c: 3 } ]
Here can test it

Adding to the accepted answer, a running code snippet with ES6.
let input = [{ a: 1 }, { b: 2 }, { c: 3 }]
//Get input object list with spread operator
console.log(...input)
//Get all elements in one object
console.log(Object.assign(...input))

I've got a neat little solution not requiring a polyfill.
var arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
var object = {};
arr.map(function(obj){
var prop = Object.getOwnPropertyNames(obj);
object[prop] = obj[prop];
});
Hope that helps :)

Here is a nice usage of Object.assign with the array.prototype.reduce function:
let merged = arrOfObjs.reduce((accum, val) => {
Object.assign(accum, val);
return accum;
}, {})
This approach does not mutate the input array of objects, which could help you avoid difficult to troubleshoot problems.

With more modern spread operator
arrOfObj.reduce( (acc, curr) => ({ ...acc, ...cur }) );

You can easily flat your object to array.
function flatten(elements) {
return elements.reduce((result, current) => {
return result.concat(Array.isArray(current) ? flatten(current) : current);
}, []);
};

6 years after this question was asked.
Object.assign is the answer (above) I like the most.
but is this also legal ?
let res = {};
[{ a: 1 }, { b: 2 }, { c: 3 }].forEach(val => {
let key = Object.keys(val);
console.log(key[0]);
res[key] = val[key];
})

const data = [
[{ a: "a" }, { b: "b" }, { c: "c" }],
[{ d: "d" }, { e: "e" }, { f: "f" }],
[{ g: "g" }, { h: "h" }, { i: "i" }],
];
function convertToObject(array){
const response = {};
for (let i = 0; i < array.length; i++) {
const innerArray = array[i];
for (let i = 0; i < innerArray.length; i++) {
const object = innerArray[i];
const keys = Object.keys(object);
for (let j = 0; j < keys.length; j++) {
const key = keys[j];
response[key] = object[key];
}
}
}
return response;
}
console.log(convertToObject(data));

function carParts(manufacturer, model, ...parts) {
return { manufacturer, model, ...Object.assign(...parts) };
}
console.log(
carParts(
"Honda",
"2008",
{ color: "Halogen Lights" },
{ Gears: "Automatic Gears" },
{ LED: "Android LED" },
{ LED: "Android LED1" }
)
);
This is how i have done.

Related

Dynamically sort object by key provided in a variable

What's wrong with this code? Why cant it sort the array according to the a key of the object?
It works when using dot notation in the condition but I want to use the second parameter in the condition instead of dot notation. Please help me getting the result.
This works:
if (objSort[i][`sortBy`] > objSort[j][`sortBy`]) {
[objSort[i], objSort[j]] = [objSort[j], objSort[i]]
}
This does not work:
function sortData(objSort, sortBy) {
for (let i = 0; i < objSort.length; i++) {
for (let j = i + 1; j < objSort.length; j++) {
if (objSort[i][`sortBy`] > objSort[j][`sortBy`]) {
[objSort[i], objSort[j]] = [objSort[j], objSort[i]]
}
}
}
console.log(data);
}
const data = [{ a: 4, b: 5 }, { a: 1, b: 10}, { a: 2, b: 5 }, { a: 14, b: 15 }, { a: 12, b: 3 }];
sortData(data,'a');
sortBy is your variable name, so remove it from the template literal:
if (objSort[i][sortBy] > objSort[j][sortBy]) {
That being said, your code is much more complex than it needs to be - there's no need for two loops and reorganising the array - just use sort():
const sortData = (objSort, sortBy) => objSort.sort((a, b) => a[sortBy] - b[sortBy]);
const data = [{ a: 4, b: 5 }, { a: 1, b: 10}, { a: 2, b: 5 }, { a: 14, b: 15 }, { a: 12, b: 3 }];
const result = sortData(data,'a');
console.log(result);

How to loop and push on the accumulator of a reduce function?

I'm trying to reduce an array, and transform it in multiple array.
const array = [
{ a: 1, b: 6 },
{ a: 1, b: 5 },
{ a: 1, b: 6 },
{ a: 1, b: 4 },
{ a: 1, b: 5 }
];
var newArray = array.reduce(
(memo, curr) => {
memo.forEach((item, key) => {
const found = item.filter((el) => el.a === curr.a && el.b === curr.b);
if (found.length > 0) return memo[key].push(curr);
else return memo.push([curr]);
});
return memo;
},
[[]]
);
The needed result I try to get is
[
[
{ a: 1, b: 5 },
{ a: 1, b: 5 }
],
[
{ a: 1, b: 6 },
{ a: 1, b: 6 },
],
[
{ a: 1, b: 4 },
]
];
But as you can see if you try, because I push on the memo, the loop continue to fire. And the result contain hundreds arrays.
How I'm supposed to do to limit this loop and get the right result ?
Thanks a lot in advance :)
You could use Map to group the element by the key of {a, b}, and then get the values of the group
const array = [
{ a: 1, b: 6 },
{ a: 1, b: 5 },
{ a: 1, b: 6 },
{ a: 1, b: 4 },
{ a: 1, b: 5 },
];
var newArray = Array.from(
array
.reduce((map, curr) => {
const key = JSON.stringify({ a: curr.a, b: curr.b });
if (!map.has(key)) {
map.set(key, []);
}
map.get(key).push(curr);
return map;
}, new Map())
.values()
);
console.log(newArray);
Look at your code. You have a triple nested loop, which is insane and definitely not needed to achieve this. Why not use a map?
Here is a function that will do what you want to do with any array of objects given.
const array = [
{ a: 1, b: 6 },
{ a: 1, b: 5 },
{ a: 1, b: 6 },
{ a: 1, b: 4 },
{ a: 1, b: 5 },
];
const separate = (arr) => {
const reduced = arr.reduce((acc, curr) => {
const path = JSON.stringify(curr);
if (!acc[path]) acc[path] = [];
acc[path].push(curr);
return acc;
}, {});
return Object.values(reduced);
};
console.log(separate(array));
If you push inside for loop it will going to push for every reduce function iteration also.
you can achieve by adding some local variables like here
const array = [
{ a: 1, b: 6 },
{ a: 1, b: 5 },
{ a: 1, b: 6 },
{ a: 1, b: 4 },
{ a: 1, b: 5 }
];
// shift changes the orginal array
// it will remove and return firstElement
var firstElement = array.shift(1);
var newArray = array.reduce(
(memo, curr) => {
let isFound = false;
let index = 0;
memo.forEach((item, key) => {
const found = item.filter((el) => el.a === curr.a && el.b === curr.b);
if(found.length > 0){
index = key;
isFound = true;
return;
}
});
if(isFound) {
memo[index].push(curr);
} else {
memo.push([curr]);
}
return memo;
},
[[firstElement]]
);
console.log(newArray);

How to group by the same key in a collection [duplicate]

This question already has answers here:
How can I group an array of objects by key?
(32 answers)
Closed 3 years ago.
Assume the following objects
let oldArr = [
{ a: 1 },
{ a: 2 },
{ a: 3 },
{ b: 1 },
{ b: 2 },
{ c: 1 }
]
Desired result
let newArr = [
[
{ a: 1 },
{ a: 2 },
{ a: 3 },
],
[
{ b: 1 },
{ b: 2 },
],
[
{ c: 1 }
],
]
I try to use lodash, I see the partition function but it only splits the arrays into 2 groups. The groupBy groups it into an object by keys.
Is there any good way? hope to get everyone's help, thank you!
you can do this using Object.keys, Object.values and Array.reduce
let oldArr = [
{ a: 1 },
{ a: 2 },
{ a: 3 },
{ b: 1 },
{ b: 2 },
{ c: 1 },
{ a: 4, c: 2 }
]
let newArr =
// drop the keys of the object created by the reduce
Object.values(
// for each object in the array
oldArr.reduce((acc, el) => {
// for each keys in the object
Object.keys(el).forEach(key => {
// add the object to the group of objects with this key
acc[key] = acc[key] || []
acc[key].push(el)
})
return acc
}, {})
)
console.log(newArr)
if an object in the input have multiple keys it will go to each group in the output
Simply like this:
let oldArr = [
{ a: 1 },
{ a: 2 },
{ a: 3 },
{ b: 1 },
{ b: 2 },
{ c: 1 }
]
let objects = {};
for (let item of oldArr) {
// get key
let key = Object.keys(item)[0];
// check if this key added before
if (!objects[key]) objects[key] = [];
// push this object
objects[key].push(item);
}
let newArr = Object.values(objects);
console.log(newArr)
But you should be sure that every object in oldArr has only one key.
you can use like that
let oldArr = [
{ a: 1 },
{ a: 2 },
{ a: 3 },
{ b: 1 },
{ b: 2 },
{ c: 1 }
];
let newArr = {};
oldArr.forEach((i)=>{
let key = Object.keys(i)[0];
newArr[key] = newArr[key] || [];
newArr[key].push(i);
});
newArr = Object.values(newArr);
console.log(newArr);

From an array of objects, how to return property `b` of the object that has the highest property `a`?

I need to get the value of the property b from the object with the highest value of the property a.
var myArr = [
{
a: 1,
b: 15
},
{
a: 2,
b: 30
}
];
I tried the following, but it just returns the highest value of a, rather than of b.
var res = Math.max.apply(Math,myArr.map(function(o){return o.a;});
var blah = getByValue(myArr);
Use Array#reduce, and on each iteration take the object with the highest a value:
var myArr = [{"a":1,"b":15},{"a":2,"b":30}];
var result = myArr.reduce(function(o, o1) {
return o.a > o1.a ? o : o1;
}).b;
console.log(result);
Can sort a copy then get first or last depending on sort direction:
var myArr = [
{
a: 1,
b: 15
},
{
a: 2,
b: 30
}
];
var highest = myArr.slice().sort((a,b)=>a.a-b.a).pop().b
console.log(highest)
Using reduce
You can use .reduce() to find the element with the maximum a value and then just grab its b value, like this:
var myArr = [{
a: 1,
b: 15
},
{
a: 2,
b: 30
}
];
var max = myArr.reduce(function(sum, value) {
return (sum.a > value.a) ? sum : value;
}, myArr[0]);
console.log(max.b);
Using sort
A bit more unorthodox approach is to use .sort() to sort the array in descending order in terms of its property a and then get the first element's b value, like this:
var myArr = [{
a: 1,
b: 15
},
{
a: 2,
b: 30
}
];
var max = myArr.sort(function(value1, value2) {
return value1.a < value2.a;
})[0];
console.log(max.b);
Try this:
var myArr = [
{
a: 1,
b: 15
},
{
a: 2,
b: 30
}
];
var res = myArr.map(function(o){return o.b;});
myArr.sort(function (o1, o2) {
return o1.a < o2.a;
})
console.log(myArr[0].b);

Return an Array of Arrays containing objects that share a common value in a property

Say I have an array of 3 objects like this:
[
{
a: 4,
b: 5,
c: 4
},
{
a: 3,
b: 5,
c: 6
},
{
a: 2,
b: 3,
c: 3
}
]
I would like to return an array of arrays containing the objects that share a common value for the property b. So the resulting array would contain only one array containing 2 objects like this:
[
[
{
a: 4,
b: 5,
c: 4
},
{
a: 3,
b: 5,
c: 6
}
]
]
How would I do this?
You could do this with map and filter
var data = [{"a":4,"b":5,"c":4},{"a":3,"b":5,"c":6},{"a":2,"b":3,"c":3}];
var check = data.map(e => {return e.b});
var result = [data.filter(e => { return check.indexOf(e.b) != check.lastIndexOf(e.b)})];
console.log(result)
To group multiple objects in separate arrays with same b values you can use map and forEach
var data = [{"a":4,"b":5,"c":4},{"a":3,"b":5,"c":6},{"a":2,"b":3,"c":3}, {"a":3,"b":7,"c":6},{"a":2,"b":7,"c":3}], result = [];
var check = data.map(e => {return e.b});
data.forEach(function(e) {
if(check.indexOf(e.b) != check.lastIndexOf(e.b) && !this[e.b]) {
this[e.b] = [];
result.push(this[e.b]);
}
(this[e.b] || []).push(e);
}, {});
console.log(result)
This proposal uses a single loop with Array#forEach but without Array#indexOf.
var array = [{ a: 4, b: 5, c: 4 }, { a: 3, b: 5, c: 6 }, { a: 2, b: 3, c: 3 }],
grouped = [];
array.forEach(function (a) {
this[a.b] = this[a.b] || [];
this[a.b].push(a);
this[a.b].length === 2 && grouped.push(this[a.b]);
}, Object.create(null));
console.log(grouped);
You can create a function that accepts fulfillment criteria and will return as many nested arrays as rules passed.
Let's say you have an array of objects, arr.
var arr = [{a: 1, b: 2}, {a: 3, b: 2}, {a: 3, b: 4}, {a: 1, b: 1}]
And you want to return an array with with nested arrays that fulfill a particular requirement, let's say you want objects with an a:1 and b:2.
You can create a function that loops through your rules and creates a nested array with the objects that fulfill each rule.
For example:
var arr = [{a: 1, b: 2}, {a: 3, b: 2}, {a: 3, b: 4}, {a: 1, b: 1}]
function makeNestedArrays() {
var rules = [].slice.call(arguments);
return rules.reduce(function(acc, fn) {
var nestedArr = [];
arr.forEach(function(obj) {
if (fn(obj)) {
nestedArr.push(obj);
}
});
// only push nested array
// if there are matches
if (nestedArr.length) {
acc.push(nestedArr);
}
return acc;
}, []);
}
var result = makeNestedArrays(
function(obj) { return obj.a === 1; },
function(obj) { return obj.b === 2; }
);
console.log(result);
This allows you to pass as many "rules" as you want, and will create a nested array for each rule so long as there is at least one match.
You could use a Map to group them, this should work with any kind of value (just be sure the equality rules check out):
var arr = [{
a: 4,
b: 5,
c: 4
}, {
a: 3,
b: 5,
c: 6
}, {
a: 2,
b: 3,
c: 3
}];
var result = arr.reduce(function(m, o){
var value = o.b;
if(m.has(value)){
m.get(value).push(o);
} else {
m.set(value, [o]);
}
return m;
}, new Map());
console.log(...(result.values()));
If you'd need to filter out the groups of 1:
var arr = [{
a: 4,
b: 5,
c: 4
}, {
a: 3,
b: 5,
c: 6
}, {
a: 2,
b: 3,
c: 3
}];
var result = arr.reduce(function(m, o){
var value = o.b;
if(m.has(value)){
m.get(value).push(o);
} else {
m.set(value, [o]);
}
return m;
}, new Map());
result = [...result.values()].filter(a => a.length > 1);
console.log(result);

Categories

Resources