I'm new to javascript and react and working on my first mini project but I cannot figure out how to solve this.
In each "Order" model I have this array field called "orderItems" which holds an array of product models and within each product model there's a field called "shop". I want to get the "shop" value for all the products in orderItems and add it into a new array in "Order" model
Here is my schema:
{
orderItems: [
{
name: 'abc',
quantity: 1,
shop: 'abc',
image: '/images/cake.jpg',
price: 50,
_id: new ObjectId("62dbb03e90c4ca14ee3e06fd")
},
{
name: 'def',
quantity: 1,
shop: 'def',
image: '/images/cake.jpg',
price: 50,
_id: new ObjectId("62dbb03e90c4ca14ee3e06fd")
}
],
sellers: [],
shippingAddress: {
fullName: '123',
address: '123',
city: '123',
postalCode: '123',
country: '123'
},
paymentMethod: 'Paypal',
itemsPrice: 50,
shippingPrice: 10,
totalPrice: 60,
buyer: new ObjectId("62d6e48d8cd5cd2e62f6aeae"),
_id: new ObjectId("62dbcf88cf1bc8fc7b85cc0b"),
__v: 0
}
So I want to get 'abc', and 'def' from orderItems array and fill it in the sellers array. I can get the values but how do I fill it into the sellers array in the same model?
this is my api code
orderRouter.post(
'/', isAuth, expressAsyncHandler(async(req, res) => {
const newOrder = new Order({
orderItems: req.body.orderItems.map((x) => ({ ...x, product: x._id })),
shippingAddress: req.body.shippingAddress,
paymentMethod: req.body.paymentMethod,
itemsPrice: req.body.itemsPrice,
shippingPrice: req.body.shippingPrice,
totalPrice: req.body.totalPrice,
buyer: req.user._id,
sellers: [],
});
const order = await newOrder.save();
})
);
I tried using a loop and then order.sellers.push(order.orderItems[i].shop) and then saving again but its not updating in my database & in local storage the _id field was pushed into the array instead of shop
Step by step:
Declare a separated const orderItems above const newOrder.
Extract const sellers using a Set. You can also do it with an array or even an object, but Set will filter out duplicates automatically.
The create the order, using the 2 consts above, and remember to turn the Set into an array.
Should look something like this:
const orderItems = req.body.orderItems.map((x) => ({ ...x, product: x._id }));
const sellers = [...new Set(orderItems.map(x => x.shop))];
const newOrder = new Order({
orderItems,
sellers: [...sellers],
shippingAddress: req.body.shippingAddress,
paymentMethod: req.body.paymentMethod,
itemsPrice: req.body.itemsPrice,
shippingPrice: req.body.shippingPrice,
totalPrice: req.body.totalPrice,
buyer: req.user._id,
});
And here's a working example:
const orderItems = [{
name: 'P1',
quantity: 1,
shop: 'S1',
image: '/images/p1.jpg',
price: 50,
_id: '62dbb03e90c4ca14ee3e06fa',
}, {
name: 'P2',
quantity: 1,
shop: 'S2',
image: '/images/p2.jpg',
price: 50,
_id: '62dbb03e90c4ca14ee3e06fb',
}, {
name: 'P3',
quantity: 1,
shop: 'S1',
image: '/images/p3.jpg',
price: 50,
_id: '62dbb03e90c4ca14ee3e06fc',
}];
const sellersSet = new Set(orderItems.map(x => x.shop));
const sellersArray = [...sellersSet];
// See here why the conversion to array is needed:
console.log({ sellersSet, sellersArray });
Related
lets say that you make a call to an API and get back an array of values like this
{
orders: [
{
id: '159151',
owner: 'steve',
created_at: '1662518452935217224',
}
]
}
to pull the id value I would normally write
const id = orders.orders[0].id
. sometimes however I get multiple back at the same time. it ranges from 1 - unknown and need to pull and input all the ids .
{
orders: [
{
id: '159151',
owner: 'steve',
created_at: '1662518452935217224',
},
{
id: '159152',
owner: 'john',
created_at: '1662518628829631781',
},
{
id: '159164',
owner: 'bob',
created_at: '1662519501403450193',
}
]
}
what can you do if you need to pull multiple id values at the same time but your unsure of the amount of id values that will be returned when you make the call to the API?
Assuming all the id keys in your example were the same (rather than idx). How about mapping them to an array?
const response = {
orders: [
{
id: '159151',
owner: 'steve',
created_at: '1662518452935217224',
},
{
id: '159152',
owner: 'john',
created_at: '1662518628829631781',
},
{
id: '159164',
owner: 'bob',
created_at: '1662519501403450193',
}
]
}
const ids = response.orders.map(order => order.id);
const count = ids.length
console.log(ids, count)
Use Array.prototype.map:
const data = { orders: [{ id: 12345 }, { id: 56789 }, { id: 192837 }] }
const ids = data.orders.map(o => o.id)
console.log(ids)
You can simply achieve that with a single line of code with the help of Array.map() method.
Live Demo :
const obj = {
orders: [
{
id: '159151',
owner: 'steve',
created_at: '1662518452935217224',
},
{
id: '159152',
owner: 'john',
created_at: '1662518628829631781',
},
{
id: '159164',
owner: 'bob',
created_at: '1662519501403450193',
}
]
};
const idsArr = obj.orders.map(({ id }) => id);
console.log(idsArr);
I need some idea!
Is there any possible way To separate user orders based on vendor email? I am trying to develop a multi-vendor project where vendors can be able to upload products. I am trying to do when a user orders from a different shop the orders need to be split based on vendor email.
Suppose a customer trying to buy from x vendor and y vendor products. When customers order the products the data look like the below array objects. It is difficult to show vendor orders in their dashboard who ordered your product. So I am trying to split the order based on email also the amount will be divided between the vendors from paymentDetail.amount when splitting the order.
[
{
_id: "622d70a49bd88b1599026318",
products: [
{
_id: "6223186e2278d4e502f5264a",
title: "Product number 1",
price: 600,
cartQuantity: 1,
vendor: {email: "vendor1#gmail.com"}
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
},
],
paymentDetails: {
createdId: 1647145079,
date: "Sun Mar 13 2022",
amount: 700,
email: "user#gmail.com",
last4: "4242",
transaction: "p"
},
status: "Pending",
billing: {
country: "BD",
name: "Md. Fathe Karim",
phone: "+88010000000",
line1: "Madhabdi",
city: "Narshingdi",
postal_code: "1604",
state: "Bandarban"
}
}]
This is my POST request from frontend:
await fetch('https://guarded-ocean-73313.herokuapp.com/dashboard/orders', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
products: [...cart], paymentDetails: {
createdId: paymentIntent.created,
date,
amount: paymentIntent.amount,
email: emailRef.current?.value,
billing: paymentIntent.billing_details,
last4: paymentMethod.card.last4,
transaction: paymentIntent?.client_secret.slice('_secret')[0]
},
status: 'Pending',
billing: {
country: countryRef.current?.value,
name: nameRef.current?.value,
phone: phoneRef.current?.value,
line1: addressRef.current?.value,
city: cityRef.current?.value,
postal_code: zipRef.current?.value,
state: stateRef.current?.value,
}
})
})
.then(res => res.json())
This is my order API
app.post('/dashboard/orders', async (req, res) => {
const productDetail = req.body
const result = await unityMartOrdersCollection.insertOne(productDetail)
res.json(result)
})
My expectation is something like this:
[
{
_id: "622d70a49bd88b1599026318", // Vendor 1 Order
products: [
{
_id: "6223186e2278d4e502f5264a",
title: "Product number 1",
price: 600,
cartQuantity: 1,
vendor: {email: "vendor1#gmail.com"}
}
],
paymentDetails: {
createdId: 1647145079,
date: "Sun Mar 13 2022",
amount: 600, // price redcuded because we divided the product
email: "user#gmail.com",
last4: "4242",
transaction: "p"
},
status: "Pending",
billing: {
country: "BD",
name: "Md. Fathe Karim",
phone: "+88010000000",
line1: "Madhabdi",
city: "Narshingdi",
postal_code: "1604",
state: "Bandarban"
}
},
{
_id: "622d70a49bd88b1599026319", // Vendor 2 Order
products: [
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
},
],
paymentDetails: {
createdId: 1647145079,
date: "Sun Mar 13 2022",
amount: 200, // price redcuded because we divided the product
email: "user#gmail.com",
last4: "4242",
transaction: "p"
},
status: "Pending",
billing: {
country: "BD",
name: "Md. Fathe Karim",
phone: "+88010000000",
line1: "Madhabdi",
city: "Narshingdi",
postal_code: "1604",
state: "Bandarban"
}
}
]
I think it's possible by the reduce method?
can someone give me any idea how can I be able to display the vendor's order into their dashboard? If my thinking is wrong you can share your idea.
This may be one possible solution to achieve the desired objective:
Code Snippet
const groupByVendor = arr => (
arr.map(order => (
Object.values( // sub-objective 1 - get values from object
order.products.reduce(
(fin, p) => ({
...fin,
...(
[p.vendor.email] in fin
? {
[p.vendor.email]: {
...fin[p.vendor.email],
products: fin[p.vendor.email].products.concat([{...p}]),
paymentDetails: {
...structuredClone(fin[p.vendor.email].paymentDetails),
amount: fin[p.vendor.email].paymentDetails.amount + p.price
} // sub-objective 2 - add price to existing amount
}
}
: {
[p.vendor.email]: {
...structuredClone(order), // sub-objective 3 - add order info here
paymentDetails: {
...structuredClone(order.paymentDetails),
amount: p.price // sub-objective 2 - set amount to price
},
products: [{...p}]
}
}
)
}),
{}
)
)
)).flat()
);
const rawData = [{
_id: "622d70a49bd88b1599026318",
products: [{
_id: "6223186e2278d4e502f5264a",
title: "Product number 1",
price: 600,
cartQuantity: 1,
vendor: {
email: "vendor1#gmail.com"
}
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
},
],
paymentDetails: {
createdId: 1647145079,
date: "Sun Mar 13 2022",
amount: 700,
email: "user#gmail.com",
last4: "4242",
transaction: "p"
},
status: "Pending",
billing: {
country: "BD",
name: "Md. Fathe Karim",
phone: "+88010000000",
line1: "Madhabdi",
city: "Narshingdi",
postal_code: "1604",
state: "Bandarban"
}
}];
console.log(groupByVendor(rawData));
Explanation
The idea is to break down the desired objective into simpler, more-manageable sub-objectives.
Sub-objective 1: A dictionary mapping vendors with orders
First, generate an object with props as the vendor email.
The values will be the actual order corresponding to the vendor
Once this object is created, all we need is the values array
Sub-objective 2: A mechanism to update the paymentDetailss amount
If processing a vendor that is not already present, set amount to price
If vendor already present, add price to existing amount
Sub-objective 3: Transform the structure of the object
The target requires each object in the array have paymentDetails, billing etc.
So, when generating the values for the vendor-object/dictionary/map, account for the corresponding transformations
The sub-objectives are marked in the code-snippet for reference.
Please use comments to ask any questions, clarifications, or suggest improvements.
NOTE
This answer employs structuredClone in order to perform deep-cloning of the order object.
My reference was Jeremy's this answer as well as this one
Modified Above Answer to Just Split Products without using structured clone , hope this might help
const groupByVendor = arr => (
arr.map(order => (
Object.values( // sub-objective 1 - get values from object
order.products.reduce(
(previousProduct, product) => ({
...previousProduct,
...(
[product.owner] in previousProduct
? {
[product.owner]: {
...previousProduct[product.owner],
products: previousProduct[product.owner].products.concat([{...product}]),
// sub-objective 2 - add price to existing amount
}
}
: {
[product.owner]: {
// sub-objective 3 - add order info here
products: [{...product}]
}
}
)
}),
{}
)
)
)).flat()
);
const rawData = [{
_id: "622d70a49bd88b1599026318",
products: [{
_id: "6223186e2278d4e502f5264a",
title: "Product number 1",
price: 600,
cartQuantity: 1,
owner: "vendor1#gmail.com"
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
owner: "vendor2#gmail.com"
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
owner: "vendor2#gmail.com"
},
]
}];
console.log(JSON.stringify(groupByVendor(rawData)));
I have a nested array of objects. I'm trying to group product objects that have the same value.
Each objects has a vendor property containing an email. I am trying to group the objects by matching vendor email
This is how my database looks like:
[
{
_id: "622d70a49bd88b1599026318",
products: [
{
_id: "6223186e2278d4e502f5264a",
title: "Product number 1",
price: 600,
cartQuantity: 1,
vendor: {email: "vendor1#gmail.com"}
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
}
]
}]
I am trying to do it with the reduce method but the problem is with using map inside the reduce. It repeats the object many times. I am also unable to get the grouped objects.
const groupedMap = db.reduce(
(entryMap, e) => e.products.map((product) => entryMap.set(product.vendor.email, [...entryMap.get(product)||[], product])),
new Map()
);
The above code output is:
My expectation is:
[0: {"vendor1#gmail.com" => Array(1)}
key: "vendor1#gmail.com"
value: [{_id: '6223186e2278d4e502f5264a', title: 'Product number 1', price: 600, cartQuantity: 1, vendor: {email: "vendor1#gmail.com"}}],
1: {"vendor2#gmail.com" => Array(2)}
key: "vendor2#gmail.com"
value: [{_id: '6223186e2278d4e502f5264a', title: 'Product number 1', price: 600, cartQuantity: 1, vendor: {email: "vendor2#gmail.com"}},
{_id: '6223186e2278d4e502f5264a', title: 'Product number 1', price: 600, cartQuantity: 1, vendor: {email:"vendor2#gmail.com"}}
]
]
Loop through each item in the array and see if the vendor email exists as a key already in a dictionary, if it exists push it to that array, otherwise set the value of the vendor email key equal to an array with the current item in it
See code below
const data = [{
_id: "622d70a49bd88b1599026318",
products: [{
_id: "6223186e2278d4e502f5264a",
title: "Product number 1",
price: 600,
cartQuantity: 1,
vendor: {
email: "vendor1#gmail.com"
}
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
},
{
_id: "622d4e9f9bd88b1599026317",
title: "asdas",
price: 100,
cartQuantity: 5,
vendor: {
email: "vendor2#gmail.com"
}
}
]
}];
const mapped = {};
data[0].products.forEach(item => {
if (item.vendor.email in mapped) return mapped[item.vendor.email].push(item);
mapped[item.vendor.email] = [item];
});
const expectedFormat = Object.keys(mapped).map(key => {
const o = {};
o[key] = mapped[key];
return o;
});
console.log(expectedFormat)
try to make an object where key is the value you group by. the next step is foreach in which you check each object in the array, whether the value you are looking for is in the object made - if not, then you filter the array by searched value and add the result to your object
I have the following code with the following arrays. I want to loop through both of them and pull out some data, and put them inside a final array. I am able to do that, but the contents are duplicated. I tried reading about reduce but don't quite understand it, and am not sure if it's the right solution. I have also setup a jsfiddle
https://jsfiddle.net/anders_kitson/Lcqn6fgd/
var lineItems = [{
id: 'li_1HyhAZHk5l44uIELgsMWqHqB',
object: 'item',
amount_subtotal: 7500,
amount_total: 7500,
currency: 'cad',
description: 'The Spencer',
price: [Object],
quantity: 1
},
{
id: 'li_1HyhAZHk5l44uIELeNUsiZPu',
object: 'item',
amount_subtotal: 7500,
amount_total: 7500,
currency: 'cad',
description: 'The Gertie',
price: [Object],
quantity: 1
}
]
var arr = [{
id: 'prod_IS1wY1JvSv2CJg',
object: 'product',
active: true,
attributes: [],
created: 1606248785,
description: 'Shelf Set',
images: [
'https://files.stripe.com/links/fl_test_raNEqk9ZhzX3WdQsnvXX4gFq'
],
livemode: false,
metadata: {},
name: 'The Spencer',
statement_descriptor: null,
type: 'service',
unit_label: null,
updated: 1606248785
},
{
id: 'prod_IS299dMnC13Ezo',
object: 'product',
active: true,
attributes: [],
created: 1606249543,
description: 'Shelf Set',
images: [
'https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe'
],
livemode: false,
metadata: {},
name: 'The Gertie',
statement_descriptor: null,
type: 'service',
unit_label: null,
updated: 1606249543
}
];
let productArr = [];
arr.map((item) => {
lineItems.map((line) => {
productArr.push({
image: item.images[0],
name: item.name,
price: line.amount_total,
});
});
});
console.log(productArr);
This is the output I get where you can see the array repeats the values, and I know I have coded it this way I just don't know how to fix it.
[{
image: "https://files.stripe.com/links/fl_test_raNEqk9ZhzX3WdQsnvXX4gFq",
name: "The Spencer",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_raNEqk9ZhzX3WdQsnvXX4gFq",
name: "The Spencer",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
}]
To Be more clear this is the output that I want
[{
image: "https://files.stripe.com/links/fl_test_raNEqk9ZhzX3WdQsnvXX4gFq",
name: "The Spencer",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
},
]
I have tried the suggestion in the comments with the following
let b
arr.map((item) => {
b = lineItems.map((line) => {
return {
image: item.images[0],
name: item.name,
price: line.amount_total,
};
});
});
but it returns the same ones twice
[{
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
}]
Although not expressed directly in your question, it seems you're looking to do a join in javascript. The only things I see relating the two are 'name' in products and 'description' in the line items. So do a loop join on that.
Here's some sample code using your example but stripped down only to what's relevant:
var lineItems = [
{ amount_total: 7500, description: 'The Spencer' },
{ amount_total: 7500, description: 'The Gertie' }
]
var arr = [
{ images: ['Spencer Image 1'], name: 'The Spencer' },
{ images: ['Gertie Image 1'], name: 'The Gertie' }
]
let joined = arr
.flatMap(a => lineItems.map(li => ({a, li})))
.filter(obj => obj.a.name == obj.li.description)
.map(obj => ({
image: obj.a.images[0],
name: obj.a.name,
price: obj.li.amount_total
}));
console.log(joined);
Being a loop join, it may not be that efficient. To do a hash join is a little more involved. You can look through the source code of my developing project
fluent-data, or it might even be useful to you to use it directly if you can follow the documentation.
You can use a single map call and reference your second lineItems array either by index, if you know that the two arrays are the same length and order
const output = arr.map((o, i) => ({
name: o.name,
image: o.images[0],
price: lineItems[i].amount_total}
));
or by using find() to retrieve the relevant object.
const outputUsingFind = arr.map(o => {
const lineItem = lineItems.find(item => item.description === o.name);
// ** add lineItem valid check here **
return {
name: o.name,
image: o.images[0],
price: lineItem.amount_total};
});
var lineItems = [{amount_subtotal: 7500,amount_total: 700,description: 'The Spencer',},{amount_subtotal: 7500,amount_total: 500,description: 'The Gertie',}];
var arr = [{images: ['spencer image'],name: 'The Spencer',},{images: ['gertie image'],name: 'The Gertie'}];
// since your arrays are ordered the same you can access the second object using
// the index passed from map.
const output = arr.map((o, i) => ({
name: o.name,
image: o.images[0],
price: lineItems[i].amount_total}
));
console.log(output);
// if the two arrays are not in the same order you can use find() to retrieve
// the second object by property (you'll need to check
const outputUsingFind = arr.map(o => {
const lineItem = lineItems.find(item => item.description === o.name);
// ** add lineItem valid check here **
return {
name: o.name,
image: o.images[0],
price: lineItem.amount_total};
});
console.log(outputUsingFind);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an large array of objects with some keys:values, one of the keys has a value that is an array of objects. I want to reduce the subarray into a new array of objects.
I can't figure out a solution using mapping thus far.
const warehouse = [
{
Server: 'EU',
Department: 'Paper',
Suppliers: [
{
Name: 'EU Paper',
Contract: 'Active'
},
{
Name: 'Local Tree',
Contract: 'Ended'
}
]
},
{
Server: 'US',
Department: 'Steel',
Suppliers: [
{
Name: 'Steel Research',
Contract: 'Active'
},
{
Name: 'Heat Vantage',
Contract: 'Active'
}
]
}
]
Output should be
const suppliers = [
{
Server: 'EU',
Department: 'Paper',
Name: 'EU Paper',
Contract: 'Active'
},
{
Server: 'EU',
Department: 'Paper',
Name: 'Local Tree',
Contract: 'Ended'
},
{
Server: 'US',
Department: 'Steel',
Name: 'Steel Research',
Contract: 'Active'
},
{
Server: 'US',
Department: 'Steel',
Name: 'Heat Vantage',
Contract: 'Active'
},
]
I can do this with basic JavaScript but i would like to see an option that optimizes for performance
You can use flatMap to loop thru the array and flat the result. Use map to loop thru the Suppliers array.
const warehouse = [{"Server":"EU","Department":"Paper","Suppliers":[{"Name":"EU Paper","Contract":"Active"},{"Name":"Local Tree","Contract":"Ended"}]},{"Server":"US","Department":"Steel","Suppliers":[{"Name":"Steel Research","Contract":"Active"},{"Name":"Heat Vantage","Contract":"Active"}]}];
let result = warehouse.flatMap(({Suppliers,...r}) => Suppliers.map(o => ({ ...o,...r})));
console.log(result);
You can also use concat and map
const warehouse = [{"Server":"EU","Department":"Paper","Suppliers":[{"Name":"EU Paper","Contract":"Active"},{"Name":"Local Tree","Contract":"Ended"}]},{"Server":"US","Department":"Steel","Suppliers":[{"Name":"Steel Research","Contract":"Active"},{"Name":"Heat Vantage","Contract":"Active"}]}];
let result = [].concat(...warehouse.map(({Suppliers,...r}) => Suppliers.map(o => ({ ...o,...r}))));
console.log(result);