How do I group an array of objects by subobject? - javascript

I'm using TypeScript for the back-end of a web app, and I've found the intersection types quite useful for making efficient SQL queries. Basically, if I have the following tables:
User
userId: number
userEmail: string
Post
postId: number
userId: number (FK)
postBody: string
I can end up with an intersection type (User & Post) that looks like this:
{
userId: number;
userEmail: string;
postId: number;
postBody: string;
}
This means I can use this type to represent the rows I get back from a joined select query.
The problem is that I have to then pull apart the data in the web server. I have to write iterative code to group them for every query, and this can get repetitive. Here's the kind of transformation I'm trying to get:
In:
[
{
userId: 1,
userEmail: 'user1#email.com',
postId: 1,
postBody: 'User 1\'s first post',
},
{
userId: 1,
userEmail: 'user1#email.com',
postId: 2,
postBody: 'User 1\'s second post',
},
{
userId: 2,
userEmail: 'user2#email.com',
postId: 3,
postBody: 'User 2\'s first post',
},
]
Out:
[
{
userId: 1,
userEmail: 'user1#email.com',
posts: [
{
postId: 1,
postBody: 'User 1\'s first post',
},
{
postId: 2,
postBody: 'User 1\'s second post',
}
],
},
{
userId: 2,
userEmail: 'User 2\'s email',
posts: [
{
postId: 3,
postBody: 'User 2\'s first post',
}
]
}
]
I'm trying to come up with a function I can use to do this dynamically, perhaps passing in the collection, an array of parent key names, and the name of the child collection. I ended up with an unsuccessful function with the following signature: function group(coll: Array<any>, parentKeys: Array<string>, childCollName: string): Array<any>;
I was wondering if anybody could help me out with implementing this.
So far, I've already tried using Lodash. However, its groupBy function doesn't seem to be able to tell that the subobjects are equal and it still gives me an array of three objects in this example.

How about trying something like below, going in loop and creating objects
var arry = [
{
userId: 1,
userEmail: 'user1#email.com',
postId: 1,
postBody: 'User 1\'s first post',
},
{
userId: 1,
userEmail: 'user1#email.com',
postId: 2,
postBody: 'User 1\'s second post',
},
{
userId: 2,
userEmail: 'user2#email.com',
postId: 3,
postBody: 'User 2\'s first post',
},
];
function createPost(obj) {
post = {};
post.postId = obj.postId;
post.postBody = obj.postBody;
return post;
}
function convert(array) {
var map = {};
for (var i = 0; i < array.length; i++) {
var currentObject = array[i];
if (!map[currentObject.userId]) {
obj = {}
obj.userId = currentObject.userId;
obj.userEmail = currentObject.userEmail;
obj.posts = [];
map[obj.userId] = obj;
}
obj.posts.push(createPost(currentObject));
}
var keys = Object.keys(map);
return keys.map(function (v) { return map[v]; });
}
var r = convert(arry)
console.log(r);

You can use Array.prototype.reduce() to achieve the goal.
var source =
[
{
userId: 1,
userEmail: 'user1#email.com',
postId: 1,
postBody: 'User 1\'s first post',
},
{
userId: 1,
userEmail: 'user1#email.com',
postId: 2,
postBody: 'User 1\'s second post',
},
{
userId: 2,
userEmail: 'user2#email.com',
postId: 3,
postBody: 'User 2\'s first post',
},
]
var grouped = source.reduce(function(v,k){
if (!v[k.userId]) {
v[k.userId]={};
}
var group = v[k.userId];
group.userId=k.userId;
group.userEmail=k.userEmail;
if(!group.posts){
group.posts=[];
}
group.posts.push({postId: k.postId,
postBody:k.postBody})
return v;
},{})
var dataArray = [];
for(var o in grouped) {
if (grouped.hasOwnProperty(o)) {
dataArray.push(grouped[o]);
}
}
console.log(JSON.stringify(dataArray,null, 2));

Here's another take with reduce, some and destructuring
DEMO
const grouped = [
{
userId: 1,
userEmail: 'user1#email.com',
postId: 1,
postBody: 'User 1\'s first post',
},
{
userId: 1,
userEmail: 'user1#email.com',
postId: 2,
postBody: 'User 1\'s second post',
},
{
userId: 2,
userEmail: 'user2#email.com',
postId: 3,
postBody: 'User 2\'s first post',
}
];
const sorted = grouped.reduce((acc, nxt) => {
const { userId, userEmail, ...rest } = nxt;
let index;
let user;
const accHasUser = acc.some((obj, i) => {
if (obj && obj.userId === userId) {
index = i;
return true;
}
return false;
});
if (!accHasUser) {
user = { userId, userEmail, posts: [rest] };
acc.push(user);
} else {
acc[index].posts.push(rest);
}
return acc;
}, []);
console.log(JSON.stringify(sorted));

Related

Merging two arrays of objects Javascript / Typescript

I have two arrays of objects which looks something like this:
const users = [
{
status: 'failed',
actionName: 'blabla',
userId: 1,
},
{
status: 'success',
actionName: 'blablabla',
userId: 2,
},
];
Second one
const usersDetails = [
{
name: 'Joseph',
id: 1,
},
{
name: 'Andrew',
id: 2,
},
];
I want to check if userId is equal to id and if so then push the name from usersDetails into users objects. So output would look like this:
const users = [
{
status: 'failed',
actionName: 'blabla',
userId: 1,
name: 'Joseph'
},
{
status: 'success',
actionName: 'blablabla',
userId: 2,
name: 'Andrew'
}];
The easiest solution would be to do:
const users = [
{
status: 'failed',
actionName: 'blabla',
userId: 1,
},
{
status: 'success',
actionName: 'blablabla',
userId: 2,
},
];
const usersDetails = [
{
name: 'Joseph',
id: 1,
},
{
name: 'Andrew',
id: 2,
},
];
const getAllUserInfo = () => users.map(user => {
const userExtraInfo = usersDetails.find(details => details.id === user.userId)
const fullUser = {...user, ...userExtraInfo}
delete fullUser.id
return fullUser
})
console.log(getAllUserInfo())
const users = [ { status: 'failed', actionName: 'blabla', userId: 1, }, { status: 'success', actionName: 'blablabla', userId: 2, }, ];
const usersDetails = [ { name: 'Joseph', id: 1, }, { name: 'Andrew', id: 2, }, ];
const newUsers = users.map(user => {
user.name = usersDetails.find(u => u.id === user.userId)?.name;
return user;
});
console.log(newUsers);
You can try this code :
let result = users.map(user => ({...user, ...usersDetails.find(userDetail => userDetail.id == user.userId) }));
console.log(result);
If you only want to get name from the second array :
let result = users.map(user => ({...user, 'name': usersDetails.find(userDetail => userDetail.id == user.userId).name }));
If you want to get all properties exepted id ::
let result = users.map(user => {
let result = {...user, ...usersDetails.find(userDetail => userDetail.id == user.userId) }
delete result.id;
return result;
});
Hope this answer will work for you
const users = [
{
status: "failed",
actionName: "blabla",
userId: 1,
},
{
status: "success",
actionName: "blablabla",
userId: 2,
},
];
const usersDetails = [
{
name: "Joseph",
id: 1,
},
{
name: "Andrew",
id: 2,
},
];
users.map((e) => {
usersDetails.find((_e) => {
if (e.userId === _e.id) {
e.name = _e.name;
}
});
});
console.log(users);
you can do something like this using a single loop
const users = [
{
status: "failed",
actionName: "blabla",
userId: 1,
},
{
status: "success",
actionName: "blablabla",
userId: 2,
},
];
const usersDetails = [
{
name: "Joseph",
id: 1,
},
{
name: "Andrew",
id: 2,
},
];
const result = Object.values([...users, ...usersDetails].reduce((res, {userId, id,...item}) => {
const key = id || userId
return {
...res,
[key]: {...(res[key] || {userId: key}), ...item}
}
}, {}))
console.log(result);
const users = [{
status: 'failed',
actionName: 'blabla',
userId: 1,
},
{
status: 'success',
actionName: 'blablabla',
userId: 2,
},
];
const usersDetails = [{
name: 'Joseph',
id: 1,
},
{
name: 'Andrew',
id: 2,
},
];
users.forEach(each => {
const found = usersDetails.find(detail => detail.id === each.userId);
if (found) {
each.name = found.name;
}
});
console.log(users);

Object to array in Angular

I need help with this code.
I working with "#angular/cli": "~12.0.5".
The createArray method receives an object and I want to transform the object to an array, but I have an error in 'userObj [key]'. I get the object (userObj) from Firebase through an http request and I can't change its structure.
This is the error message. -> Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. No index signature with a parameter of type 'string' was found on type '{}'
Thanks!
const userObj = {
'SJKLDFAD903':{
id: '',
name: 'User 1'
},
'PLMKL-BAD89':{
id: '',
name: 'User 2'
},
'JHK34R-R903':{
id: '',
name: 'User 3'
}
}
export class UserModel{
id: string;
name: string;
}
private createArray(userObj){ /*(userObj: object)*/
const users: UserModel[] = [];
if (userObj == null) { return []; }
Object.keys(userObj).forEach(key => {
const user: UserModel = userObj[key];
user.id = key;
users.push(user);
});
return users;
}
Try this.
private createArray(userObj){ /*(userObj: object)*/
const users: UserModel[] = [];
if (userObj == null) { return []; }
for (const [key, object] of Object.entries(userObj)) {
const user: UserModel = object as UserModel;
user.id = key;
users.push(user);
}
return users;
}
yow broh.
use Object.values instead
const userObj = {
'SJKLDFAD903':{
id: '',
name: 'User 1'
},
'PLMKL-BAD89':{
id: '',
name: 'User 2'
},
'JHK34R-R903':{
id: '',
name: 'User 3'
}
}
export class UserModel{
id: string;
name: string;
}
private createArray(userObj): UserModel[] {
return Object.values(userObj)
}
const userObj = {
'SJKLDFAD903':{
id: '',
name: 'User 1'
},
'PLMKL-BAD89':{
id: '',
name: 'User 2'
},
'JHK34R-R903':{
id: '',
name: 'User 3'
}
}
function createArray(userObj) {
return Object.values(userObj)
}
console.log(createArray(userObj))

ES6 array of hashes return unique array of hashes [duplicate]

This question already has answers here:
Create array of unique objects by property
(17 answers)
Closed 3 years ago.
I have an object that looks like this:
const posts = [
{ id: 0, user: { id: 5564, name: 'john'} },
{ id: 1, user: { id: 5564, name: 'john'} },
{ id: 2, user: { id: 5560, name: 'jane'} }
]
I need an array of the unique user hashes like this:
[
{ id: 5564, name: 'john'},
{ id: 5560, name: 'jane'}
]
I'm able to retrieve all the users attributes from the posts array by doing:
const postUsers = posts.map(post => post.user)
which returns:
[
{ id: 5564, name: 'john'},
{ id: 5564, name: 'john'},
{ id: 5560, name: 'jane'}
]
where user john is listed twice
I've been able to get my desired result by doing:
const unique = {};
const uniqueUsers = [];
for(var i in postUsers){
if(typeof(unique[postUsers[i].id]) == "undefined"){
uniqueUsers.push(postUsers[i]);
}
unique[postUsers[i].id] = 0;
};
uniqueUsers
but there must be a cleaner way.
I've also been able to return the unique ids of all users by doing:
var ids = posts.map(post => post.user.id)
var uniqueIds = Array.from(new Set(ids)).sort();
which returns
[5564, 5560]
not sure if that helps. this article helped me a little https://medium.com/tomincode/removing-array-duplicates-in-es6-551721c7e53f
You could take a Map and get only the unique users.
const
posts = [{ id: 0, user: { id: 5564, name: 'john'} }, { id: 1, user: { id: 5564, name: 'john'} }, { id: 2, user: { id: 5560, name: 'jane'} }],
unique = Array.from(posts.reduce((m, { user }) => m.set(user.id, user), new Map).values());
console.log(unique);
If you don't mind using lodash you can do something like
const users = _map.(posts, 'user') // To get the list of users
_.uniqBy(users, 'id') // to get the uniq ones
Put the objects directly in uniqueUsers, then use Object.values() at the end to convert the object to an array.
const posts = [
{ id: 0, user: { id: 5564, name: 'john'} },
{ id: 1, user: { id: 5564, name: 'john'} },
{ id: 2, user: { id: 5560, name: 'jane'} }
];
let uniqueUsers = {};
posts.forEach(({user}) => uniqueUsers[user.id] = user);
uniqueUsers = Object.values(uniqueUsers);
console.log(uniqueUsers);
Use reduce to reduce the array by checking if the value is already in the array. If it is already in the array, return the current state of the array, otherwise add the item to the array.
const posts = [
{ id: 0, user: { id: 5564, name: 'john'} },
{ id: 1, user: { id: 5564, name: 'john'} },
{ id: 2, user: { id: 5560, name: 'jane'} }
]
const r = posts.map(i => i.user).reduce((acc, itm) => {
return !acc.find(i => i.id == itm.id) && acc.concat(itm) || acc
}, [])
console.log(r)

How to map two array JSON to data in vue

I have JSON data that have two different arrays in one sheet.
I want to show each Organization Page then each page have every comment from the user who selects comment this organization.
{
ListOrg: {
id: 1613,
name_org: "US company",
rating: 0,
},
Review: [
{
review_id: 3,
org_id: 1613,
user_id: 2,
description: "Very good",
rating: 3,
user: {
id: 2,
name: "Kunjanaphorn",
firstname: "Kunjanaphorn",
lastname: "Boonmak",
admin: 0,
email: "Pim#gmail.com",
}
},
{
review_id: 4,
org_id: 1613,
user_id: 3,
description: "Not bad",
rating: 5,
user: {
id: 3,
name: "Kan",
firstname: "Kan",
lastname: "Tippayamontree",
admin: 0,
email: "First#gmail.com",
}
}
]
}
But I cannot map it to data in vue.js. I try this solution then it not work.
data() {
return {
Listorgs: [],
Reviews: [],
Listorg: {
name_org: "",
picture: "",
headerpic: "",
description: ""
}
mounted() {
axios.get("/api/listorgs/" + this.id).then(response => {
var listorg = response.data;
this.Reviews = listorg.Review;
this.Listorgs = listorg.Listorg;
});
}
Can you tell me about syntax
I want some attribute in each array -> name_org(string) from array ListOrg, description(string) from array Review, name(string) and firstname(string) from user array in Review array for show in vue page
I have helped you changed a little bit and made it work, check it out.
https://jsfiddle.net/fLvy3am9/
var listorg = response.data;
this.orgData = listorg.ListOrg;
this.reviews = listorg.Review.map((review) => {
return {
name_org: listorg.ListOrg.name_org,
description: review.description,
user: review.user
};
});
Ask me if there's any question.

map function save result to an object

this is what my response looks like
data: [
{
id: 3,
name: "Oliver Green",
email: "test#gmail.com",
contacts: "09179878564"
},
{
id: 2,
name: "Orval McLaughlin",
email: "okoch#example.org",
contacts: "09083692343",
}
]
I used the map function to get the user id and user name, now what I'm trying to do is to save all of the result to an Object
data(){
return {
autoComplete:{},
}
},
let vm = this;
response.data.data.map((user) =>
{
return vm.autoComplete = { [user.id] : user.name};
});
I get the result however I'm only getting one result
autoComplete:Object
2:"Orval McLaughlin"
the result should be
autoComplete:Object
3: "Oliver Green"
2: "Orval McLaughlin"
You need to return the object from map() not the result of an assignment. You are currently assigning vm.autoComplete on each iteration. After you do this you can assign the output of map to the variable you want:
let data = [
{
id: 3,
name: "Oliver Green",
email: "test#gmail.com",
contacts: "09179878564"
},
{
id: 2,
name: "Orval McLaughlin",
email: "okoch#example.org",
contacts: "09083692343",
}
]
let autoComplete = data.map((user) => {
return { [user.id] : user.name};
});
console.log(autoComplete)
EDIT:
If you want an object instead of an array, you should use reduce() because map() always returns an array:
let data = [
{
id: 3,
name: "Oliver Green",
email: "test#gmail.com",
contacts: "09179878564"
},
{
id: 2,
name: "Orval McLaughlin",
email: "okoch#example.org",
contacts: "09083692343",
}
]
let autoComplete = data.reduce((obj, user) =>{
obj[user.id] = user.name; // this assumes user.id will be unique
return obj
}, {});
console.log(autoComplete)
Try this for not wrapping it in array.
response.data.data.map((user) => {
return vm.autoComplete = Object.assign(vm.autoComplete, {[user.id] : user.name}); }
It seems the autoComplete be overwrite for each.
Maybe you can try these:
data(){
return {
autoComplete:{},
}
},
let vm = this;
vm.autoComplete = response.data.data.map((user) => {
return { [user.id] : user.name};
});

Categories

Resources