How to use ES6 Object spread to update object inside array? - javascript

I've got the following array of objects which comes from a response:
const baseInput = [{
PaymentRequirementsDetail:
{ dateDue: '12/02/2019',
outstandingMinimum: { Money: { amount: '5.20', code: 'GBP' } },
overlimit: { Money: { amount: '345.20', code: 'GBP' } },
arrears: { Money: { amount: '345.20', code: 'GBP' } } }
},
{ Account: {},
AccountId: '00000012345',
CardBrand: 'SOMEBRAND',
isAccountElibible: false,
Customer:
{ salutation: 'Mr',
givenName: 'James',
familyName: 'Jamesy',
suffix: 'Dr' },
Delinquency: { monthsInArrears: 0, isOverlimit: true } }]
I am then transforming the response with a bunch of functions and am returning a friendly, formatted version of the above.
const baseOutput = transform(baseInput);
This returns:
{ name: 'Mr James Jamesy, Dr',
cardBrand: 'SOMEBRAND',
isAccountElibible: false,
delinquency: { monthsInArrears: 0, isOverlimit: true },
dateDue: '12/02/2019',
outstandingMinimumAmount: 'GBP, 5.20',
overlimitAmount: 'GBP, 345.20',
arrearsAmount: 'GBP, 345.20' }
I would now like to test this and generate a few snapshots.
I can copy/paste the above code into my test-cases and change values as I do my assertions which works fine. Like this;
test('should omit suffix if it is undefined', () => {
const input = [{
PaymentRequirementsDetail:
{ dateDue: '12/02/2019',
outstandingMinimum: { Money: { amount: '5.20', code: 'GBP' } },
overlimit: { Money: { amount: '345.20', code: 'GBP' } },
arrears: { Money: { amount: '345.20', code: 'GBP' } } }
},
{ Account: {},
AccountId: '00000012345',
CardBrand: 'SOMEBRAND',
isAccountElibible: true,
Customer:
{ salutation: 'Mr',
givenName: 'James',
familyName: 'Jamesy' },
Delinquency: { monthsInArrears: 0, isOverlimit: true } }];
const output = transform(input);
expect(baseOutput).toMatchDiffSnapshot(output);
});
This will generate my snapshot as I require it and I will be able to see the difference between the version with a suffix and the version without one clearly.
However I believe that there is a cleaner way to do this using the object spread operator. Instead of all of the above code, I should be left with;
const input = [{
...baseInput,
Customer:
{ salutation: 'Mr',
givenName: 'James',
familyName: 'Jamesy'
}
}];
I am unable to however utilise the spread operator in a way so that I can achieve that. Can anyone see where my mistake is?

Your baseInput is an Array with two items. The spread operator works on either arrays or objects, what you are doing here is spreading the array into your target object.
If your model does not change, you could simply spread the indexed object into your target like so:
const input = [{
...baseInput[0]
},{
...baseInput[1],
Customer:
{ salutation: 'Mr',
givenName: 'James',
familyName: 'Jamesy'
}
}];
https://stackblitz.com/edit/typescript-imcqkh?file=index.ts

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;
});

Is there a way to transform a JavaScript object into another object with cleaner formatting?

When accessing a public register API, I receive more information than I need, and sometimes the data is returned with minor variations. I would like to delete some unnecessary fields, move nested fields to the top level, and rename them. The goal is to standardise format across several different APIs, and keep the memory requirement to a minimum. Example below:
Raw object:
[
{
startDate: "2022/08/27",
expiryDate: "2025/08/27",
party: {
type: "Business",
name: "Irregular Expressions Inc."
},
location: {
type: "Office",
address: {
locality: "Boston",
postcode: "PE21 8QR"
}
}
},
{
startDate: "2023/12/22",
expiryDate: "2024/06/22",
party: {
type: "Charity",
name: "Save the Badgers"
},
site: {
type: "Office",
address: {
locality: "Badgerton",
postcode: "BA6 6ER"
}
}
},
]
I want to transform this into a smaller, cleaner array:
[
{
startDate: "2022/08/27",
expiryDate: "2025/08/27",
partyName: "Irregular Expressions Inc.",
location: "Boston"
},
{
startDate: "2023/12/22",
expiryDate: "2024/06/22",
partyName: "Save the Badgers",
location: "Badgerton"
},
]
I have tried the below, but I'm getting an error.
module.exports = {
testTransform: (inputArray) => {
const outputArray = []
inputArray.forEach(element => {
outputArray.push({
startDate: element.startDate,
expiryDate: element.expiryDate,
partyName: element.party.name,
location: element.location.address.locality
})
})
return JSON.stringify(outputArray, null, ' ')
}
}
TypeError: Cannot read properties of undefined (reading 'address')
Am I going in the right direction, or is there a simpler way of doing this? I've searched for this type of transformation but with no luck - what am I missing?
You could take either location or site with logical OR || and later the proerties with optional chaining operator ?..
const
data = [{ startDate: "2022/08/27", expiryDate: "2025/08/27", party: { type: "Business", name: "Irregular Expressions Inc." }, location: { type: "Office", address: { locality: "Boston", postcode: "PE21 8QR" } } }, { startDate: "2023/12/22", expiryDate: "2024/06/22", party: { type: "Charity", name: "Save the Badgers" }, site: { type: "Office", address: { locality: "Badgerton", postcode: "BA6 6ER" } } }],
result = data.map(o => ({
startDate: o.startDate,
expiryDate: o.expiryDate,
partyName: o.party.name,
location: (o.location || o.site)?.address?.locality
}));
console.log(result);
Since it looks like you don't know what the outer key will be for the object with the address property, if the object will always have 4 properties, when destructuring, you can use rest syntax to collect the final property into a single object, and then take that object's values to get to the address.
const input=[{startDate:"2022/08/27",expiryDate:"2025/08/27",party:{type:"Business",name:"Irregular Expressions Inc."},location:{type:"Office",address:{locality:"Boston",postcode:"PE21 8QR"}}},{startDate:"2023/12/22",expiryDate:"2024/06/22",party:{type:"Charity",name:"Save the Badgers"},site:{type:"Office",address:{locality:"Badgerton",postcode:"BA6 6ER"}}}];
const output = input.map(({
startDate,
expiryDate,
party,
...rest
}) => ({
startDate,
expiryDate,
partyName: party.name,
location: Object.values(rest)[0].address.locality,
}));
console.log(output);
You are trying to read locality property of undefined. You could use optional chaining operator to prevent the exception throwing. So, you need to use somthing like element?.location?.address?.locality instead of element.location.address.locality.
That would require writing a function that recurcively goes throught the contents of an object an returns a non-nested object. The function below is such a function.
const flattenObject = (obj) => {
let result = {};
for (const i in obj) {
if ((typeof obj[i]) === 'object') {
const temp = flattenObject(obj[i]);
for (const j in temp) {
result[j] = temp[j];
}
}
else {
result[i] = obj[i];
}
}
return result;
};
The function can then be called on each nested object in the array. The map method of arrays would be do nicely for that step.
const result = nested.map(n => flattenObject(n))
console.table(result[0]) would produce the output below

Why the structure of mongoose subdocument can be different?

Is it expected behaviour of mongoose, or it shouldn't be like this. So I have 2 schema for users and address. The schema looks like these:
const addressSchema = {
addressLine: { type: String},
city: { type: String},
locations: { type: String, coordinates: [Number] }, <-- subdocument
}
const userSchema = {
name: { type: String },
email: { type: String },
addresses: {
primary: { type: AddressSchema },
others: { type: [AddressSchema] }
}
}
And I try to insert this data:
{
name: "John Doe",
addresses: {
primary: {
addressLine: "Apple Street"
},
others: [
{
addressLine: "Mango Street"
}
]
}
}
Now this is the confusing part. So I didn't include the locations in the input. But the data that saved in my DB looks like this:
{
name: "john Doe",
addresses: {
primary: {
addressLine: "Apple Street"
},
others: [
{
addressLine: "Mango Street",
locations: { <-- this locations suddenly shown!
coordinates: []
}
}
]
}
_id: randomString,
__v: 0
}
Please help, and thank you in advance :D
In this case, locations is automatically added in your subdocument because of coordinates field in it.
Whenever you define an array enter code heretype field in your schema, Mongoose automatically initialises it with empty array if the value is not provided.
And,since you are not passing any value to locations.coordinates, it is initializing it as an empty array.
If you want to test this theory, please remove coordinates from locations, and you will see the difference.

Targeting Specific Array Element with Assignment Destructuring

I am using some assignment destructuring in my MongoDB/Node backend in order to handle some post-processing. I'm just trying to understand how this destructuring works, and if, in the case of an array of multiple elements and nested arrays, if I can input the element I want to target.
Take for instance this code:
services: [
,
{
history: [...preSaveData]
}
]
} = preSaveDocObj;
My assumption is that the "," in "services" for the above code will default to looking at the first element in the array. Correct?
Now, if I have a document structure that looks like this (see below), and I know I want to target the "services" element where "service" is equal to "typeTwo", how would I do that?:
{
_id: 4d39fe8b23dac43194a7f571,
name: {
first: "Jane",
last: "Smith"
}
services: [
{
service: "typeOne",
history: [
{ _id: 121,
completed: true,
title: "rookie"
},
{ _id: 122,
completed: false,
title: "novice"
}
]
},
{
service: "typeTwo",
history: [
{ _id: 135,
completed: true,
title: "rookie"
},
{ _id: 136,
completed: false,
title: "novice"
}
]
}
]
}
How can I edit this code (see below) to specifically target the "services" array where "service" is equal to "typeTwo"?
services: [
,
{
history: [...preSaveData]
}
]
} = preSaveDocObj;
Don't overdestructure, just find:
const { history: [...preSavedData] } = doc.services.find(it => it.serice === "typeTwo");

MongoDb's $ (update) does not update array's element but rather replace it?

I want to update an element of an array inside mongodb's document (I am using mongoose). Schema is something like:
{
..
arr : [{
foo: Number,
bar: [String],
name: String
}]
..
}
And my query is:
SomeModel.update({
_id: "id of the document",
arr: {
$elemMatch: {
_id: "_id assigned by mongoose to array element"
}
}
}, {
'arr.$': {
name: 'new name'
}
}).exec()
It just replaces whole array element say:
{
_id: "some objectId",
name: 'old name',
foo: 0,
}
to:
{
name: 'new name'
}
what I want:
{
_id: "some objectId",
name: 'new name',
foo: 0,
}
What I am curious to know if it is possible to achieve this in single update query ? (May be there is a silly mistake in my query :P or another approach)
I would also like to do update query like so:
{
$inc: { foo: 1},
$push: { bar: "abc"}
}
If you are still struggling with the whole implementation the full application of your statement is as follows:
SomeModel.update(
{
"arr._id": "123"
},
{
"$set": { "arr.$.name": "new name" },
"$inc": { "arr.$.foo": 1},
"$push": { "arr.$.bar": "abc" }
}
)
,function(err,numAffected) {
});
So each operation is performed in turn.

Categories