I'm looking for the best solution to merge all objects in one array
const arrayOfObjects = [
{name: 'Fred', surname: 'Shultz'}, {name: 'Anne', surname: 'Example'}
];
I want to achieve: {name: ['Fred', 'Anne'], surname: ['Example', 'Shultz']}
What's the best option for that (es6)? Maybe I can do something like that using lodash? Which helpers should I use?
You could reduce the array by iterating the entries and collecting the values, depending of the keys.
const
array = [{ name: 'Fred', surname: 'Shultz' }, { name: 'Anne', surname: 'Example' }],
result = array.reduce((r, o) => {
Object.entries(o).forEach(([k, v]) => (r[k] = r[k] || []).push(v));
return r;
}, Object.create(null));
console.log(result);
You could do it like this:
const arrayOfObjects = [
{name: 'Fred', surname: 'Shultz'}, {name: 'Anne', surname: 'Example'}
];
const result = {};
arrayOfObjects.forEach(item => {
Object.keys(item).forEach(key => {
if (!result[key]) {
result[key] = [];
}
result[key].push(item[key]);
});
});
console.log(result);
easy with lodash:
grouped = _.mapValues(arrayOfObjects[0],
(val, key) => _.map(arrayOfObjects, key))
pure es6
let grouped = {};
for (let obj of arrayOfObjects)
for (let [key, val] of Object.entries(obj))
grouped[key] = (grouped[key] || []).concat(val)
if the keys differ from item to item, you could use something like this to collect them all:
grouped = _(arrayOfObjects)
.flatMap(_.entries)
.groupBy(0)
.mapValues(x => _.map(x, 1))
.value()
You can use lodash's mergeWith like so:
const result = _.mergeWith({}, ...arrayOfObjects, (value, objValue) =>
(value || []).concat(objValue)
);
Example:
const arrayOfObjects = [
{name: 'Fred', surname: 'Shultz'}, {name: 'Anne', surname: 'Example'}
];
const result = _.mergeWith({}, ...arrayOfObjects, (value, objValue) =>
(value || []).concat(objValue)
);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Short way with array reduce:
const arrayOfObjects = [
{name: "name1", surname: "surname1"}, {name: 'Anne', surname: 'Example'}, {name: 'name3', surname: 'Example3'}
];
/*
{name: ['Fred', 'Anne'], surname: ['Example', 'Shultz']}
*/
var result = arrayOfObjects.reduce((obj,current)=>{
(obj['name'] = obj['name']||[]).push(current.name);
(obj['surname'] = obj['surname']||[]).push(current.surname);
return obj;
},{});
console.log(result);
Don't make it any more complicated than it needs to be:
const arrayOfObjects = [
{name: 'Fred', surname: 'Shultz'},
{name: 'Anne', surname: 'Example'}
];
const result = {name:[], surname:[]};
for (const obj of arrayOfObjects)
for (const prop in result)
result[prop].push(obj[prop]);
I will assume that you statically know the property names that your result should have - one can't really do it dynamically anyway as that wouldn't work properly for an empty input array.
Here is a lodash approach
_(input).flatMap(_.entries).groupBy(0).mapValues(v => _.map(v, 1)).value()
var input = [
{name: 'Fred', surname: 'Shultz'}, {name: 'Anne', surname: 'Example'}
];
var res = _(input).flatMap(_.entries).groupBy(0).mapValues(v => _.map(v, 1)).value()
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
This will take care if the objects doesn't have exactly same key sets
If the arrayOfObjects is set on these 2 props then it is as simple as:
const data = [{ name: 'Fred', surname: 'Shultz' }, { name: 'Anne', surname: 'Example' }]
const r = data.reduce((r,c) =>
(r.name.push(c.name), r.surname.push(c.surname),r), {name:[], surname:[]})
console.log(r)
One reduce with an accumulator of {name:[], surname:[]} to be filled.
If you need to be more generic and work for any set of objects:
const data = [{
name: 'Fred',
surname: 'Shultz'
},{
name: 'Anne',
surname: 'Example'
},{
name: 'John',
position: 'Dev' // <--- Notice different prop
}]
const result = data.reduce((r,c) =>
(Object.keys(c).map(k => r[k] = [...r[k] || [], c[k]]), r), {})
console.log(result)
Again is just a reduce with Object.keys to do the job.
Note both approaches utilize ES6 arrow functions, array destricturing and (for the 2nd one) combining multiple operations via enclosing them in parentheses (op1,op2)
This is one abroach of implementation details, written in fairly easy to understand and readable manner.
https://codesandbox.io/s/r7x16j950n
const arrayOfObjects = [
{ name: "Fred", surname: "Shultz" },
{ name: "Anne", surname: "Example" }
];
let obj = {};
arrayOfObjects.forEach(row => {
Object.keys(row).forEach(key => {
obj[key] = !obj[key]
? [row[key]]
: [...obj[key], row[key]];
});
});
console.log(obj);
The following should work - uses a few ES6 helpers, but the key is Array#reduce which is in ES5.
const result = arrayOfObjects.reduce((acc, obj) => {
for (let key in obj) {
if (key in acc) {
acc[key].push(obj[key]);
}
else {
acc[key] = [obj[key]];
}
}
return acc;
}, {});
Without any library
const mergeObjectInArray=(input)=>{
const myObj={};
Object.keys(input[0]).forEach(key=>myObj[key]=input.map(inp=>inp[key]));
return myObj;
}
with pure javascript
var myInput = [{ a: 1, b: 2, c: 3 }, { a: 2, b: 4, c: 6 }, { a: 7, b: 8, c: 9 }];
var myArray = [];
var myObject = {};
function isArray(a){
return Object.prototype.toString.call(a) === '[object Array]' ;
}
for (var i = 0; i < myInput.length; i++) {
for (var key in myInput[i]) {
if (myInput[i].hasOwnProperty(key)) {
if (myArray.indexOf(key) === -1) {
myArray.push(key);
myObject[key] = myInput[i][key];
} else {
if (myObject.hasOwnProperty(key)) {
newary = [];
if (isArray(myObject[key])) {
for (var i = 0; i < myObject[key].length; i++) {
newary.push(myObject[key][i]);
}
} else {
newary.push(myObject[key]);
}
newary.push(myInput[i][key]);
myObject[key] = newary;
}
}
}
}
}
console.log(myObject);
Related
I have the following source array:
const list = [
{
students: [ 'peter', 'bob', 'john']
},
{
students: [ 'thomas', 'sarah', 'john']
},
{
students: [ 'john', 'sarah', 'jack']
}
];
and i want to get the unique student names and their count, final result should be like:
{
'john': 3,
'sarah': 2,
'thomas': 1,
'jack': 1,
'peter': 1,
'bob': 1
}
here is my attempt:
const unique = list.reduce(function(total, curr){
const students = curr.students;
for (c of students) {
if (!total[c]) {
total[c] = 1
} else {
total[c] += 1;
}
}
return total;
}, {});
is there a better way to do it? or faster and cleaner way? thanks
I'd flatten the arrays first, then count up with reduce:
const list = [
{
students: [ 'peter', 'bob', 'john']
},
{
students: [ 'thomas', 'sarah', 'john']
},
{
students: [ 'john', 'sarah', 'jack']
}
];
const allStudents = list.flatMap(({ students }) => students);
const count = allStudents.reduce((a, name) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {});
console.log(count);
If you want the properties to be ordered as well, then take the Object.entries of the object, sort it, then turn it back into an object with Object.fromEntries:
const list = [
{
students: [ 'peter', 'bob', 'john']
},
{
students: [ 'thomas', 'sarah', 'john']
},
{
students: [ 'john', 'sarah', 'jack']
}
];
const allStudents = list.flatMap(({ students }) => students);
const count = allStudents.reduce((a, name) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {});
const sorted = Object.fromEntries(
Object.entries(count).sort((a, b) => b[1] - a[1])
);
console.log(sorted);
If your environment doesn't support flatMap, or fromEntries, use a polyfill, or flatten/group with a different method:
const list = [
{
students: [ 'peter', 'bob', 'john']
},
{
students: [ 'thomas', 'sarah', 'john']
},
{
students: [ 'john', 'sarah', 'jack']
}
];
const allStudents = [].concat(...list.map(({ students }) => students));
const count = allStudents.reduce((a, name) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {});
const sortedEntries = Object.entries(count).sort((a, b) => b[1] - a[1]);
const sortedObj = sortedEntries.reduce((a, [prop, val]) => {
a[prop] = val;
return a;
}, {});
console.log(sortedObj);
Keep in mind that object property order is only specified in ES6+ environments. While Object.fromEntries isn't guaranteed by the specification to create an object in the same order as the entries, it does anyway, in any implementation I've ever encountered, luckily. (If you're still worried about it, you can use the old-fashioned reduce method to create the object instead, like in the third snippet)
Try using functional programming: combination of map and 2 reduce methods.
const listMapped = list.map(it=> it.students)
const listReduced = listMapped.reduce((acc, rec) => {
return [...acc.concat(rec)]
}, [])
const listCounted = listReduced.reduce((acc, rec) => {
acc[rec]
? acc[rec] += 1
: acc[rec] = 1
return acc
}, {})
console.log(listCounted)
Suppose I got this array:
const users =[
{
id:1,
name:'bob',
},
{
id:2,
name:'sally',
},
{
id:3,
name:'bob',
age:30,
}
];
And I want to use any key(in this case 'name' ) to return an object :
{
bob:[
{
id:1,
name:'bob',
},
{
id:3,
name:'bob',
age:30,
}
],
sally:[
{
id:2,
name:'sally',
}
],
}
I tried this:
const go = (A,key) =>{
return A.reduce((o, key) => ({ ...o, [key]:o }), {})
}
export default go;
But this returns:
{ '[object Object]': { '[object Object]': { '[object Object]': {} } } }
If the key is not present omit from the result. It should not mutate the original array though. How can I perform this kind of conversion?
With the approach you have, a new array is not instantiated in case the key is not yet present in the object.
This will work:
const result = users.reduce((a, v) => {
a[v.name] = a[v.name] || [];
a[v.name].push(v);
return a;
}, {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => {
a[v[key]] = a[v[key]] || [];
a[v[key]].push(v);
return a;
}, {});
console.log(go(users, 'name'));
If you really want to cram it into a one-liner, this will also work, by either spreading the already existing array, or an empty one:
const result = users.reduce((a, v) => ({...a, [v.name]: [...a[v.name] || [], v]}), {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => ({...a, [v[key]]: [...a[v[key]] || [], v]}), {});
console.log(go(users, 'name'));
You were close but the key attribute in this case was each value (eg: { id: 1, name: 'bob' }) so the string representation is [object Object] which is why all the keys are that. Based off what you said, you want to use key.name as the property and set it's value as [key]. (I renamed key to arr in my example since it's the array value).
So this would be something like { ...o, [arr.name]: [arr] }
Because there can be an existing value, it adds a bit of complexity which is what [...(obj[arr.name] || []), arr] is doing. It's looking up the existing value (or defaulting to an empty array) and spreading those values and adding the new value.
const users = [{
id: 1,
name: 'bob',
},
{
id: 2,
name: 'sally',
},
{
id: 3,
name: 'bob',
age: 30,
}
];
const transform = (input, keyName) => {
return input.reduce((obj, arr) => ({ ...obj,
[arr[keyName]]: [...(obj[arr[keyName]] || []), arr]
}), {})
}
console.log(transform(users, 'name'))
console.log(transform(users, 'id'))
Let say I have json like this (use JSON.stringify)
{ name: 'Bill', lastname: 'Smith'}
And I want the value wrapped with curly braces like this
{ name: { value: 'Bill' }, lastname: { value: 'Smith'} }
So any idea to do like this using javascript or lodash?
I'd use Object.entries on the input, map to a nested object, then call Object.fromEntries to transform it back again:
const input = { name: 'Bill', lastname: 'Smith'};
const newObj = Object.fromEntries(
Object.entries(input).map(
([key, value]) => ([key, { value }])
)
);
console.log(newObj);
Object.fromEntries is a pretty new method, so for older browsers, either include a polyfill or use something like .reduce instead:
const input = { name: 'Bill', lastname: 'Smith'};
const newObj = Object.entries(input).reduce(
(a, [key, value]) => {
a[key] = { value };
return a;
},
{}
);
console.log(newObj);
You can loop through the keys of the object using for...in and update it like this:
const input = { name: 'Bill', lastname: 'Smith'};
for (const key in input) {
input[key] = { value: input[key] }
}
console.log(input)
If you don't want to mutate the input and want to create a new object, then create another object and update it:
const input = { name: 'Bill', lastname: 'Smith'},
output = {}
for (const key in input) {
output[key] = { value: input[key] }
}
console.log(output)
You can use lodash's _.mapValues() to return a new object with transformed values:
const object = { name: 'Bill', lastname: 'Smith'};
const result = _.mapValues(object, value => ({ value }));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
I have a array like structure from database results
var arr = [{name: 'a', age: 23}, {name: 'b', age: 24}, {name: 'c', age: 35}]
I want to create a new object with values as key and value like below:
var new_arra = {a: 23, b: 24, c: 35}
How can I do this in lodash?
I tried the below method:
var result = _.forEach(results, function(index) {
var result = _.map(index, function(value, prop) {
return {prop: prop, value: value};
});
});
Using ES5 - iterate with Array#reduce with initial value of an empty object. On each iteration set the name as key, and the age as value.
var arr = [{name: 'a', age: 23}, {name: 'b', age: 24}, {name: 'c', age: 35}];
var result = arr.reduce(function(obj, o) {
obj[o.name] = o.age;
return obj;
}, {});
console.log(result);
Using ES6 - Iterate with Array#map get the values using destructuring, and set using computed property names. Combine all to a single object by using Object#assign with the spread syntax:
const arr = [{name: 'a', age: 23}, {name: 'b', age: 24}, {name: 'c', age: 35}];
const result = Object.assign({}, ...arr.map(({ name, age }) => ({ [name]: age })));
console.log(result);
You can do it with pure JS this way:
var arr = [{
name: 'a',
age: 23
}, {
name: 'b',
age: 24
}, {
name: 'c',
age: 35
}]
var result = {};
arr.forEach(function(item) {
result[item.name] = item.age
});
console.log(result);
Another way with reduce:
var arr = [{
name: 'a',
age: 23
}, {
name: 'b',
age: 24
}, {
name: 'c',
age: 35
}];
var result = arr.reduce(function(store, item) {
store[item.name] = item.age;
return store;
}, {});
console.log(result);
you need these two line only
let newData = {};
arr.map((item)=>newData[item.name]=item.age);
console.log(newData);
or you can also do it using lodash
let newData = {};
_.map(arr,(item)=>newData[item.name]=item.age);
That's simple.
All you need to do is loop through each data and insert it into a new empty object.
Ex.
let new_arra = {};
_.each(arr, a => {
new_arra[a.name] = a.age
});
I have an array of strings:
var array = ['bob', 'charlie', 'bob', 'bob'];
that I want to remove duplicates from, and then I'd like to convert it to an array of objects whilst adding a duplicate count property.
This is what I want to achieve:
var filteredData = [{ name: 'bob', count: 3}, { name: 'charlie', count: 1}];
How can I do that?
Ecmascript5 solution using Array.prototype.reduce() function:
var arr = ['bob', 'charlie', 'bob', 'bob'],
counts = arr.reduce(function(r,s){
(!r[s])? r[s] = {name: s, count: 1} : r[s]['count']+=1;
return r;
}, {}),
result = Object.keys(counts).map(function(k){ return counts[k]; });
console.log(result);
Ecmascript6 version (with Object.values() function):
var arr = ['bob', 'charlie', 'bob', 'bob'],
result = Object.values(arr.reduce((r,s) => {
(!r[s])? r[s] = {name: s, count: 1} : r[s]['count']+=1;
return r;
}, {}));
console.log(result);
Using Map with Array#reduce:
const array = ['bob', 'charlie', 'bob', 'bob'];
const result = [...array.reduce((map, name) => {
const current = map.get(name) || { name, count: 0 };
current.count++;
return map.set(name, current);
}, new Map()).values()];
console.log(result);
Here is some straightforward usage of Array.prototype.reduce:
const data = ['bob', 'charlie', 'bob', 'bob']
const result = data.reduce(function(prev, curr) {
const index = prev.findIndex(el => el.name === curr)
if (index !== -1) {
prev[index].count += 1
} else {
prev.push({ name: curr, count: 1 })
}
return prev
}, [])
console.log(result)