How can I merge an array and group by key in Javascript? - javascript

I have multiple array in reactjs.
{last_name: 'User1', status: 'BRONZE', type: 'Maintenance', due_date: '2022-06-04 00:00:00'}
{last_name: 'User1', status: 'BRONZE', type: 'Contrôle technique', due_date: '2022-06-18 00:00:00'}
{last_name: 'User2', status: 'BRONZE', type: 'Unknow', due_date: null}
I would like to merge the array by user last_name to have a result like this:
{last_name: 'User1', status1: 'BRONZE', type: 'Maintenance1', due_date1: '2022-06-04 00:00:00', status2: 'BRONZE', type2: 'Contrôle technique', due_date: '2022-06-18 00:00:00'}
{last_name: 'User2', status: 'BRONZE', type: 'Unknow', due_date: null}
In my example I have merge the array 1 and 2 to have 1 array "group by" last_name, here User1 but I need to keep the value of the second array too.

Your question lacks a few details. Are status and due_date the only fields that might be repeated? If so, the below answer should work. If not, you might want to specify which keys should be merged with an index in the field name, and which can be pulled in as is.
I'm not sure why you would want to structure your data this way-- having different field names seems like it would make it difficult to find the data, but leaving that aside:
const data = [{
last_name: 'User1',
status: 'BRONZE',
type: 'Maintenance',
due_date: '2022-06-04 00:00:00'
}, {
last_name: 'User1',
status: 'BRONZE',
type: 'Contrôle technique',
due_date: '2022-06-18 00:00:00'
}, {
last_name: 'User2',
status: 'BRONZE',
type: 'Unknow',
due_date: null
}]
const mergedMap = {}
// Group list elements by last_name
for (const el of data) {
if (el.last_name in mergedMap) {
mergedMap[el.last_name].push(el)
} else {
mergedMap[el.last_name] = [el]
}
}
// Iterate over "user" groups, modifying field names.
const mergedList = []
for (const last_name in mergedMap) {
const elCount = mergedMap[last_name].length
// If there's only one entry for this "last_name", keep it as is,
// then continue to next user.
if (elCount === 1){
mergedList.push(mergedMap[last_name][0])
continue
}
const mergedUser = mergedMap[last_name].reduce((merged, el, index) => ({
// Keep whatever keys are already here
...merged,
// last_name and status are assumed to always be the same
// for a given user, so they're safe to overwrite each time
last_name: el.last_name,
status: el.status,
// type and due_date might be unique for each entry, so
// we add an index to the field name and copy the new value in
[`type${index + 1}`]: el.type,
[`due_date${index + 1}`]: el.due_date,
}), {})
mergedList.push(mergedUser)
}
console.log(JSON.stringify(mergedList, null, 2))

Related

Best way to return a nested object the matches a property requested

I'm trying to create a new object that only contains the a product array with the seller I req. I have an order object that has a product array. I'd like to return a specific seller. I tried:
const newOrders = orders.map((element) => {
return {
...element,
product: element.product.filter(
(seller) => seller === req.currentUser!.id
),
};
});
does mongoose have a preferred method for doing what I bring to achieve? I've read through the find queries but none of the methods seem useful to this use case.
orders: [
{
userId: "638795ad742ef7a17e258693",
status: "pending",
shippingInfo: {
line1: "599 East Liberty Street",
line2: null,
city: "Toronto",
country: "CA",
postal_code: "M7K 8P3",
state: "MT"
},
product: [
{
title: "new image",
description: "a log description",
seller: "6369589f375b5196f62e3675",
__v: 1,
id: "63737e4b0adf387c5e863d33"
},
{
title: "Mekks",
description: "Ple",
seller: "6369589f375b5196f62e3675",
__v: 1,
id: "6376706808cf1adafd5af32f"
},
{
title: "Meeks Prodyuct",
description: "long description",
seller: "63868795a6196afbc3677cfe",
__v: 1,
id: "63868812a6196afbc3677d06"
}
],
version: 1,
id: "6388138170892249e01bdcba"
}
],
Im sure this can be improved, doesn't feel that its the best way possible but it gets the result. Like the previous answer you have to find first the order the seller is in then find the products than filter the seller by the id. I'm using typescript and there's a bug https://github.com/microsoft/TypeScript/issues/50769 so you have to use the bracket notation.
const orders = await Order.find({
"product.seller": req.currentUser!.id,
});
const allOrders = orders[0].product;
const sellerOrders = allOrders.filter((obj) => {
return obj["seller"] === req.currentUser!.id;
});

Impossible to query nested mongoose array?

I want to Query and array with regex inside and mongoose (mongoDB) model.
I want to search inside the nested array of the Productmodel :
const productSchema = new schema(
{
name: requiredString,
sku: requiredUniqueNumber,
ean: requiredUniqueNumber,
suppliers: [{ type: mongoose.Schema.Types.ObjectId, ref: SupplierModel }],
categories: [{ type: mongoose.Schema.Types.ObjectId, ref: CategoryModel }],
mainImage: requiredString,
images: [{ type: String }],
description: requiredString,
stock: requiredNumber,
price: requiredNumber,
totalOrders: requiredNumber,
reviews: [review],
},
{
timestamps: true,
count: true,
}
);
The model inside the "suppliers" array is:
const supplierSchema = new schema(
{
supplierName: requiredUniqueString,
invoiceAddress: address,
visitAddress: address,
status: supplierStatusEnum,
contacts: address,
accountType: accountTypeEnum,
logo: requiredString,
user: { type: schema.Types.ObjectId, ref: "products" },
},
{
timestamps: true,
}
);
Now here's the problem, if i query and and populate() i get all the results. But for some reason I cannot search inside the Array containing several suppliers. Here's of what i have:
foundProducts = await ProductModel.find({
$or: [
{
name: {
$regex: regex,
},
},
{
"suppliers.supplierName": {
$regex: regex,
},
},
{
description: {
$regex: regex,
},
},
],
});
The object in JSON:
If he finds that the suppliers model contains the regex he should give back the whole porductmodel containing that supplier.
What is the best way to search in all the items inside of an nested array.
ps. I'm a junior developer comming from PostgreSQL, so bare with me while I'm trying this noSQL "thing" :)
I was doing the wrong query. I need to do
{
"suppliers._id": {
$regex: regex,
},
},
I can only search on _id, since this is the way that i "modeled" it.

Mongoose populate referencing object id, not the object itself

Background
Here's part of my User model:
const Group = require("./Group")
...
groups: {
type: [{ type: Schema.ObjectId, ref: Group }],
default: [],
},
And here's my Group model:
module.exports = mongoose.model(
"Group",
new Schema(
{
name: {
type: String,
required: true,
unique: true,
},
/**
* Array of User ObjectIDs that have owner rights on this group
*/
owners: {
type: [{ type: Schema.ObjectId, ref: User }],
default: [],
},
},
{
timestamps: true,
}
)
)
The Code
Here's the code I'm running to try and populate:
const user = await (await User.findOne({ _id: ... })).execPopulate("Group")
console.log(user.groups)
My console.log is outputting an array of object IDs, when I'd like it to output an actual Group document.
Attempted solutions
I've tried changing my ref to be using the string ("Group"), I've tried arranging my query differently, etc. I'm not sure how I'd go about doing this.
Apologies in advance if this is a duplicate, I've done my best to search but can't really find a solution that works for me.
Specifically, what do I need help with?
I'm trying to create a 'link' between a user model and a group model. In my console.log, I expect it to output a Group document; but it outputs an object ID (which is how it's stored raw in the database, meaning that Mongoose isn't transforming it correctly)
When you change execPopulate to populate like:
async function findUserAndPopulate(userId){
const response = await User.findOne({
_id: userId,
}).populate('groups')
console.log("response",response)
}
You got:
{
groups: [
{
owners: [Array],
_id: 5ecc637916a2223f15581ec7,
name: 'Crazy',
createdAt: 2020-05-26T00:31:53.379Z,
updatedAt: 2020-05-26T00:31:53.379Z,
__v: 0
}
],
_id: 5ecc6206820d583b99b6b595,
fullname: 'James R',
createdAt: 2020-05-26T00:25:42.948Z,
updatedAt: 2020-05-26T00:36:12.186Z,
__v: 1
}
So you can access the user.groups
See the doc: https://mongoosejs.com/docs/populate.html

How to populate document nested in array of objects in Mongoose?

I want to populate the ingredientsList.
Is it possible to get the array back with populated ingredients field ?
How I can do it, if I want just one object out of ingredientsList with populated ingredient ?
Hope you can help :)
My Schema looks like this:
export const RecipeSchema = new Schema({
name: {
type: String,
required: 'Enter a name',
},
ingredientsList: [
{
ingredient: {
type: Schema.Types.ObjectId,
ref: 'Ingredient',
},
value: {
type: Number,
default: 1,
},
},
],
});
My Ingredient Model looks like this:
export const IngredientSchema = new Schema({
name: {
type: String,
required: 'Enter a name',
},
created_date: {
type: Date,
default: Date.now,
},
amount: {
type: Number,
default: 1,
},
});
As the field, ingredientsList, is an array of referenced documents from the Ingredient collection - you can use model.populate() as follows:
const recipes = await Recipe.find().populate('ingredientsList');
res.send({ data: orders });
This will return all Recipes and also expand or populate the ingredientsList for each.
Let's say that you only wanted populate the name field of each document in ingredientsList - you do so as follow:
const recipes = await Recipe.find().populate('ingredientsList', 'name');
res.send({ data: orders });
This will return all Recipes but only populate the name field on ingredientsList for each.
Mongoose docs on population

Map and Filter to populate array with objects by id

I need to populate array of ids with objects. In other words I have. Array of ids:
var orderArray = ["5ace454a2b22e17597d0e694", "5acde7c0f7d2520e3b205971", "5ad2086bf05ad342dc723ea1"]
And array of objects:
var objectsArray = [ { _id: 5acde7c0f7d2520e3b205971,
name: 'Dinner',
restaurant: 5a68d8ea17d9e4308e6400c3,
created: 2018-04-11T10:47:28.957Z,
status: true,
products: [ [Object] ] },
{ _id: 5ace454a2b22e17597d0e694,
name: 'Test',
restaurant: 5a68d8ea17d9e4308e6400c3,
image:
{ _id: 5ad23ed177bcd07303f62899,
filename: 'rJKCR2k2f-1523728081111.jpeg',
destination: 'images',
binded: true },
created: 2018-04-11T17:26:34.186Z,
status: false,
products: [ [Object], [Object] ] },
{ _id: 5ad2086bf05ad342dc723ea1,
name: 'Test',
restaurant: 5a68d8ea17d9e4308e6400c3,
image: null,
created: 2018-04-14T13:55:55.449Z,
status: true,
products: [] } ]
Either you can sort array of objects based on ids... Or map array of ids to array of objects. Probably I'd prefer the second option.
But my approach just doesn't work
orderArray.map(id => objectsArray.filter(obj => obj._id == id))
The result shall be: objectsArray is sorted as order of elements in orderArray
SOLUTION: I've opened this question few days ago: Merging 2 arrays with different value types
Here I have the same problem. orderArray is array of objects (not string) thus in order to make it work I need to apply the solution I found earlier (both Array.filter and Array.find functions works well):
but in my way it will work only if:
order_array.map(String).map(e => objectsArray.find(a => a._id == e))
//as well as
order_array.map(String).map(e => objectsArray.filter(a => a._id == e))
map the first array to fill it with corresponding elements from the second one :
var orderArray = ["5ace454a2b22e17597d0e694", "5acde7c0f7d2520e3b205971", "5ad2086bf05ad342dc723ea1"]
var objectsArray = [ { _id: '5acde7c0f7d2520e3b205971',
name: 'Dinner',
restaurant: '5a68d8ea17d9e4308e6400c3',
created: '2018-04-11T10:47:28.957Z',
status: true,
products: [ [Object] ] },
{ _id: '5ace454a2b22e17597d0e694',
name: 'Test',
restaurant: '5a68d8ea17d9e4308e6400c3',
image:
{ _id: '5ad23ed177bcd07303f62899',
filename: 'rJKCR2k2f-1523728081111.jpeg',
destination: 'images',
binded: true },
created: '2018-04-11T17:26:34.186Z',
status: false,
products: [ [Object], [Object] ] },
{ _id: '5ad2086bf05ad342dc723ea1',
name: 'Test',
restaurant: '5a68d8ea17d9e4308e6400c3',
image: null,
created: '2018-04-14T13:55:55.449Z',
status: true,
products: [] } ]
var sorted = orderArray.map((e) => { return objectsArray.find((a) => { return a._id == e})})
console.log(sorted)
You should be able to:
objectsArray.filter(obj => ordersArray.includes(obj._id));
If I am understanding correctly.
Using map/find (instead of filter):
let mappedArray = orderArray.map(id => objectsArray.find(obj => obj._id == id));
which maps orderArray to an array of objects, where it finds the object from objectsArray that has the same _id as the current id.
Note: If there is no object in objectsArray that matches the id, null will be returned.

Categories