How to simplify object - javascript

I have an issue here where the code is quite heavy and quite hard to read imo.
I've simplified the code as much as I can but I was wandering if there's a way to simplify this code even further? Or maybe if there is any better terminology i could use for the comments? Any help is much appreciated, Thanks in advance!
const hourly = rateType[1] === 'Hourly';
const daily = rateType[1] === 'Day Pass';
const monthly = rateType[1] === 'Monthly Pass';
const single = passType === 'single';
// -- For all payloads
const data = {
booking_name: username.first_name + ' ' + username.last_name,
number_of_people: numOfPeople,
};
// -- Hourly payload
const hourlyPayload = {
...data,
date: moment(mainDate).format('YYYY-MM-DD'),
from: moment(timeFrom).format('hh:mm'),
to: moment(timeUntil).format('hh:mm'),
};
// -- Daily or monthly payload
const DayOrMonthPayload = {
...data,
start_date: moment(startDate).format('YYYY-MM-DD'),
end_date: moment(endDate).format('YYYY-MM-DD'),
};
// -- Single day payload
const singleDayPayload = {
...data,
dates: [moment(startDate).format('YYYY-MM-DD')],
};
// -- // CREATE_BOOKING_FN // -- //
if (rateType) {
setLoading(true);
hourly // --||-- Hourly Action --||-- \\
? await addToCart(hourlyPayload, 'hourly', id, cartItems, () =>
_handleAdded(
fastCheckout ? fastCheckout : null,
cb ? () => cb() : null,
),
)
: daily // --||-- Daily Action --||-- \\
? await addToCart(
single ? singleDayPayload : DayOrMonthPayload,
single ? 'individual-dates' : 'daily',
id,
cartItems,
() =>
_handleAdded(
fastCheckout ? fastCheckout : null,
cb ? () => cb() : console.log('null'),
),
)
: monthly // --||-- Monthly Action --||-- \\
? await addToCart(DayOrMonthPayload, 'monthly', id, cartItems, () =>
_handleAdded(
fastCheckout ? fastCheckout : null,
cb ? () => cb() : console.log('null'),
),
)
: null;
setLoading(false);
} else {
alert('Please select a rate');
}

You can use Template Strings to simplify booking_name
booking_name: `${username.first_name} ${username.last_name}`,
Also consistency in variable names would better, you could choose one of the variable naming styles, snake_case or camelCase.
Also now you can shorten expression key:value even more.
const data = {
booking_name: `${username.first_name} ${username.last_name}`,
number_of_people,
};
Also that ternary expression is very hard to read, I think switch case would better.
switch (type_of_date) {
case hourly:
...
case daily:
...
case monthly:
...
default:
...
}

I would recommend using functions for avoiding repetition and creating data. Here we have a basic Booking object that can be used to construct all varieties of bookings. Fill in ___ as you see fit -
function Booking(type, ___) {
switch (type) {
case "Hourly"
return { ...Party(___), ...Hourly(___) }
case "DayOrMonth":
return { ...Party(___), ...DayOrMonth(___) }
case Single:
return { ...Party(___), ...Single(___) }
default:
throw Error("invalid booking type: " + type)
}
}
In the function above it's plain to see that each output has Party information associated -
function Party(user, number_of_people) {
return {
name: user.first_name + " " + user.last_name
number_of_people
}
}
Here are the booking types, Hourly, DayOrMonth and Single -
function Hourly(date, from, to) {
return {
date: mDate(date),
from: mTime(from),
to: mTime(to)
}
}
function DayOrMonth(start, end) {
return {
start: mDate(start),
end: mDate(end)
}
}
function Single(date) {
return {
date: mDate(date)
}
}
We avoid repetition of moment(...).format(...) and ensure consistency with mDate and mTime -
function mDate(t) {
return moment(t).format("YYYY-MM-DD")
}
function mTime(t) {
return moment(t).format("hh:mm")
}

Related

Javascript Conditional return object

I used aws-sns to create one webhook. Two lambda functions are checked by this webhook. One of the lambda functions publishes 'orderId' and'startTime', while another publishes 'orderId' and 'roundName'. Both lambdas fire at different times. As a result, publishing can happen at two different times. One or both of the'startTime' and 'roundName' parameters may be undefined.
If 'roundName' exists, the 'updateOrder' variable will return 'roundName,' and the database will be updated. When'startTime' is set and 'roundName' is left blank, the 'roundName' will be rewritten from the database, which I don't want. Because if there is a 'roundName,' there will always be a 'roundName,' the value of 'roundName' can change but it will never be undefined.If startTime changes as well as roundName change then it will update the database. But my current logic is wrong. Struggling to implementing diffrent scenario logic.
const data = {
Records: [
{
Sns: {
Message:
'[{\n "orderId": "a4013438-926f-4fdc-8f6a-a7aa402b40ea",\n "roundName": "RO1"}]',
},
},
],
};
const existingData = [
{
modifiedAt: "2022-03-09T13:18:06.211Z",
lastMile: "progress",
createdAt: "2022-02-26T06:38:50.967+00:00",
orderId: "a4013438-926f-4fdc-8f6a-a7aa402b40ea",
},
];
// parse the data
const parseData = data.Records.flatMap((record) =>
JSON.parse(record.Sns.Message)
);
// check if the data exist or not
const existingOrder = existingData.filter(
(o1) => parseData.some((o2) => o1.orderId === o2.orderId)
);
// if there is no existingOrder then return false
if (existingOrder.length === 0) return;
// if there is exisiting order then add roundName and startTime from SNS event
const updateOrder = existingOrder.map((i) => {
const roundName = parseData.find((r) => {
return r.orderId === i.orderId;
}).roundName;
const startTime = parseData.find((r) => {
return r.orderId === i.orderId;
}).startTime;
return {
roundName: roundName ?? "",
startTime: startTime ?? "",
};
});
console.log(updateOrder);

Pagination in TypeORM/NestJS

I have to introduce pagination in findAll() method. I really dont know how to do it. I tried but it is giving so many errors. I used findAndCount() method given by typeorm for that, But I am not sure how it will work.
As of now below method returning all the record. I need to return at a time 10 records. Please suggest what modification I need to do.
async findAll(queryCertificateDto: QueryCertificateDto,page=1): Promise<PaginatedResult> {
let { country, sponser } = queryCertificateDto;
const query = this.certificateRepository.createQueryBuilder('certificate');
if (sponser) {
sponser = sponser.toUpperCase();
query.andWhere('Upper(certificate.sponser)=:sponser', { sponser });
}
if (country) {
country = country.toUpperCase();
query.andWhere('Upper(certificate.country)=:country', { country });
}
const certificates = query.getMany();
return certificates;
}
this is PaginatedResult file.
export class PaginatedResult {
data: any[];
meta: {
total: number;
page: number;
last_page: number;
};
}
I tried changing code of findAll() but where clause is giving error. I am not sure how to handle query.getMany() in pagination.
const take = query.take || 10
const skip = query.skip || 0
const [result, total] = await this.certificateRepository.findAndCount(
{
where: query.getMany(), //this is giving error
take:take,
skip:skip
}
);
return result;
I need to introduce pagination in this method. Any help will be really helpful.
Typeorm has a really nice method specific to your usecase findAndCount
async findAll(queryCertificateDto: QueryCertificateDto): Promise<PaginatedResult> {
const take = queryCertificateDto.take || 10
const skip = queryCertificateDto.skip || 0
const country = queryCertificateDto.keyword || ''
const sponser = queryCertificateDto.sponser || ''
const query = this.certificateRepository.createQueryBuilder('certificate');
const [result, total] = await this.certificateRepository.findAndCount(
{
where: { country: Like('%' + country + '%') AND sponser: Like('%' + sponser + '%') }, order: { name: "DESC" },
take: take,
skip: skip
}
);
return {
data: result,
count: total
};
}
More documentation about Repository class can be found here
You don't need the .getMany() with your where in the last code, the result is an array of the data you need.
From your first code, you can do this:
async findAll(queryCertificateDto: QueryCertificateDto,page=1): Promise<PaginatedResult> {
// let's say limit and offset are passed here too
let { country, sponser, limit, offset } = queryCertificateDto;
const query = this.certificateRepository.createQueryBuilder('certificate');
if (sponser) {
sponser = sponser.toUpperCase();
query.andWhere('certificate.sponser = :sponser', { sponser });
}
if (country) {
country = country.toUpperCase();
query.andWhere('certificate.country = :country', { country });
}
// limit and take mean the same thing, while skip and offset mean the same thing
const certificates = await query
.orderBy("certificate.id", "ASC")
.limit(limit || 10)
.offset(offset || 0)
.getMany();
// if you want to count just replace the `.getMany()` with `.getManyandCount()`;
return certificates;
}```

Typescript - failure to update array of objects when using date

I have a typescript function that receives a 'queryParameter' and then loops over an existing set of queryParameters from a state hook within the component. If it finds a matching parameter name I want it to update the queryParameters object into the const 'newQueryParams'.
note that the issue only occurs with the 'Date' query parameter. All code examples work fine for both other types ('getLatest' and 'fundCode'). Also this is not a question of date object equality
function (only the necessary part):
const handleQueryParameterChange = (queryParameter: DataProductQueryParameter) => {
console.log("existing parameters from state hook: ", queryParameters)
console.log("incoming parameter to function: ", queryParameter)
const newQueryParams = queryParameters.map((param, i) => {
console.log("loop: " + i);
if(queryParameter.name === param.name) {
console.log("match");
param.suggestedValue = queryParameter.suggestedValue;
}
return param;
});
console.log("new query params: ", newQueryParams);
I have logged the various bits to show what happens:
As you can see the incoming parameter tries to change the date to the 21st, but fails, despite a match in the loop.
I have also tried using deep copies:
const deepCopyFunction = (inObject: any) => {
let outObject: any, value, key
if (typeof inObject !== "object" || inObject === null) {
return inObject // Return the value if inObject is not an object
}
// Create an array or object to hold the values
outObject = Array.isArray(inObject) ? [] : {}
for (key in inObject) {
value = inObject[key]
// Recursively (deep) copy for nested objects, including arrays
outObject[key] = deepCopyFunction(value)
}
return outObject
}
const handleQueryParameterChange = (
queryParameter: DataProductQueryParameter
) => {
const queryParametersCopy = deepCopyFunction(queryParameters);
const deepCopyQueryParam = {...queryParameter};
console.log("existing parameters copy: ", queryParametersCopy)
console.log("incoming new parameter: ", queryParameter)
console.log("incoming parameter deep copy: ", deepCopyQueryParam);
const newQueryParams = queryParametersCopy.map((param, i) => {
console.log("loop: " + i);
if(deepCopyQueryParam.name === param.name) {
console.log("match");
param.suggestedValue = deepCopyQueryParam.suggestedValue;
}
return param;
});
console.log("new query params: ", newQueryParams);
which produced the same:
I have also tried just switching out the whole object when i get the match, rather than just editing a property:
const handleQueryParameterChange = (queryParameter: DataProductQueryParameter) => {
console.log("existing parameters: ", queryParameters)
console.log("incoming new parameter: ", queryParameter)
const newQueryParams = queryParameters.map(param => {
return queryParameter.name === param.name ? queryParameter : param;
});
console.log("new query params: ", newQueryParams);
...which didnt work either/ The below screenshot outlines the output, but also note the difference between the headline and expanded object in "incoming new parameter". I've seen this a few times and while stack overflow tells me its the async nature of the console, i can't figure out why its happening in the code.
again please note that the issue only occurs with the 'Date' query parameter. All code examples work fine for both other types ('getLatest' and 'fundCode')
Update
I have implemented one of the suggested improvements, but I'm still not getting the right output.
Here is a little more of the code, including a state hook and an effect hook, with the new implementation:
const [queryParameters, setQueryParameters] = useState<DataProductQueryParameter[]>(dataProductQueryParameters ?? []);
useEffect(() => {
console.log("effect called: ", queryParameters)
}, [queryParameters])
const handleQueryParameterChange = (queryParameter: DataProductQueryParameter) => {
if(queryParameter.name === "getLatest") {
setDatePickerDisabled(!datePickerDisabled);
}
const matchIndex = queryParameters.findIndex(param => queryParameter.name === param.name);
const queryParametersCopy = JSON.parse(JSON.stringify(queryParameters));
if (matchIndex !== -1) {
const suggestedValue = queryParameter.suggestedValue;
queryParametersCopy[matchIndex] = { ...queryParametersCopy[matchIndex], suggestedValue}
}
console.log("new query params: ", queryParametersCopy);
setQueryParameters(queryParametersCopy);
};
The state hook sets successfully on component load. When I invoke the handleChange function, my debugger shows that it updates the new array (newQueryParams) successfully.
The issue arises when setQueryParameters is called to update the state hook. It seems to trigger fine as the useEffect hook gets called. But both my debugger and console show that the suggestedValue field on the updated array doesnt get updated.
Again - this works absolutely fine for the other fields - getLatest and fundCode, so the logic is sound and the hooks are firing for setting new values, but the date one simply won't update.
Thanks
Your initially code works perfectly fine. If you run the following snippet you will see that the final new query params log show that the suggestedValue attribute has been updated successfully:
const queryParameters = [{
name: 'test',
suggestedValue: '2021-03-23'
},
{
name: 'foobar',
suggestedValue: '2022-03-23'
}
]
const handleQueryParameterChange = (queryParameter) => {
console.log("existing parameters from state hook: ", queryParameters)
console.log("incoming parameter to function: ", queryParameter)
const newQueryParams = queryParameters.map((param, i) => {
console.log("loop: " + i);
if (queryParameter.name === param.name) {
console.log("match");
param.suggestedValue = queryParameter.suggestedValue;
}
return param;
});
console.log("new query params: ", newQueryParams);
}
handleQueryParameterChange({
name: 'test',
suggestedValue: '2021-03-21'
})
However you could improve this code to avoid parsing all the elements of the array with .map(). You should better use the .findIndex() method for that. If you run the following snippet you will see that it will only log loop: 0:
const queryParameters = [{
name: 'test',
suggestedValue: '2021-03-23'
},
{
name: 'foobar',
suggestedValue: '2022-03-23'
}
]
const handleQueryParameterChange = (queryParameter) => {
console.log("existing parameters from state hook: ", queryParameters)
console.log("incoming parameter to function: ", queryParameter)
const matchIndex = queryParameters.findIndex((param, i) => {
console.log("loop: " + i);
return queryParameter.name === param.name
})
if (matchIndex !== -1) {
const suggestedValue = queryParameter.suggestedValue
queryParameters[matchIndex] = { ...queryParameters[matchIndex],
suggestedValue
}
}
console.log("new query params: ", queryParameters);
}
handleQueryParameterChange({
name: 'test',
suggestedValue: '2021-03-21'
})
I've tried to reproduce your setup with a small component having 2 states like you showed on your updated question. This code (which reuses your object swap) seems to update queryParameters properly. Could you try to reuse the part with setQueryParameters(prevQueryParameters => ...) to see if it solves your problem?
import React, { FC, Fragment, useCallback, useEffect, useState } from 'react';
interface DataProductQueryParameter {
name: string;
parameterType: string;
format: string;
description: string;
suggestedValue: string;
}
const dataProductQueryParameters: DataProductQueryParameter[] = [
{
name: 'test',
format: '',
description: '',
parameterType: '',
suggestedValue: '',
},
{
name: 'other',
format: '',
description: '',
parameterType: '',
suggestedValue: '',
},
];
export const StackOverflow: FC = () => {
const [datePickerDisabled, setDatePickerDisabled] = useState(false);
const [queryParameters, setQueryParameters] = useState<DataProductQueryParameter[]>(
dataProductQueryParameters,
);
useEffect(() => {
console.log('effect called: ', queryParameters);
}, [queryParameters]);
const handleQueryParameterChange = useCallback(
(queryParameter: DataProductQueryParameter) => {
if (queryParameter.name === 'getLatest') {
setDatePickerDisabled(prevDatePickerDisabled => !prevDatePickerDisabled);
}
setQueryParameters(prevQueryParameters =>
prevQueryParameters.map(param => {
if (param.name === queryParameter.name) {
return queryParameter;
}
return param;
}),
);
},
[setQueryParameters, setDatePickerDisabled],
);
return (
<Fragment>
<button
onClick={() =>
handleQueryParameterChange({
name: 'test',
description: 'updated',
format: 'updated',
parameterType: 'updated',
suggestedValue: 'updated',
})
}
>
Update !
</button>
</Fragment>
);
};

What is the best way for reducing multiple if statement?

I have a helper function that builds object with appropriate query properties. I use this object as a body in my promise request. What is the most elegant way for refactoring multiple if statements? Here is a function:
getQueryParams = (query, pagination, sorting) => {
let queryParam = {}
if (pagination && pagination.pageNumber) {
queryParam.page = `${pagination.pageNumber}`
}
if (pagination && pagination.rowsOnPage) {
queryParam.size = `${pagination.rowsOnPage}`
}
if (query) {
const updatedQuery = encodeURIComponent(query)
queryParam.q = `${updatedQuery}`
}
if (sorting) {
queryParam.sort = `${sorting.isDescending ? '-' : ''}${sorting.name}`
}
return service.get(`/my-url/`, queryParam).then(result => {
return result
})
}
If service checks its parameters (as it should), you could benefit from the default parameters. Something like this:
const getQueryParams = (
query = '',
pagination = {pageNumber: 0, rowsOnPage: 0},
sorting = {isDescending: '', name: ''}
) => {
const queryParam = {
page: pagination.pageNumber,
size: pagination.rowsOnPage,
q: encodeURIComponent(query),
sort: `${sorting.isDescending}${sorting.name}`
}
return ...;
};
A live example to play with at jsfiddle.
This is an idea how it could looks like, but you need to adopt your params before:
const query = new URLSearchParams();
Object.keys(params).forEach(key => {
if (params[key]) {
query.append(key, params[key]);
}
});

Taking the Average of a Data Set in Firebase Database

I'm in the process of designing an app but coming a bit unstuck with Javascript. So far I have a Firebase Realtime Database with the following structure.[!
What I'd like to do is for each time an area in green is added / updated, take a value(red) from that area in green get the average from all the values that are held within each green object and place it into a brown object at the bottom.
Would anyone have any idea on how to complete this using Javascript / Firebase functions?
JSON Export:
{
"5Rz8DpU34PeXAcnriD6vEiPu7jk2" : {
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"rating1" : 5
},
"average" : 0
},
"Fi43uP2LcbVLi2uFwUyCAp2uvSH2" : {
"average" : 0
},
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"Fi43uP2LcbVLi2uFwUyCAp2uvSH2" : {
"rating1" : 5,
"rating2" : 5
},
"asdas" : {
"rating1" : 2
},
"average" : 0
},
"gov4hRpDgDVhyVgsQrYJnn1rfeW2" : {
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"rating1" : 5
},
"average" : 0
}
}
The following Cloud Function code should do the trick:
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.database();
exports.average = functions.database
.ref('/ratings/{blueId}/{greenId}')
.onWrite((change, context) => {
const blueId = context.params.blueId;
const blueRef = db.ref('ratings/' + blueId);
const blueAverageRef = db.ref('ratings/' + blueId + '/average');
let totalSum = 0;
let nbrOfElem = 0;
return blueRef
.once('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
if (childSnapshot.val().val) {
//console.log(childSnapshot.val());
totalSum += childSnapshot.val().val;
nbrOfElem++;
}
});
})
.then(() => {
//console.log('totalSum: ' + totalSum);
//console.log('nbrOfElem: ' + nbrOfElem);
return blueAverageRef.transaction(function(average) {
if (nbrOfElem > 0) {
return { val: totalSum / nbrOfElem };
} else {
return 0;
}
});
})
.catch(error => {
console.log(error);
});
});
Note that it uses a Transaction, see https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions and https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction
The database shall be structured as:
-ratings
-blueNode1
-greenNode11
-val:2 // <- red node in your picture
-greenNode12
-val:10
-average // <- red node in your picture
-val:6
-blueNode2
-greenNode21
-val:5
-greenNode22
-val:3
-greenNode23
-val:1
-average
-val:5
It would be something like this:
exports.detectGreenChanges = functions.database.ref('/requests/{blueId}/{greenId}').onWrite((change, context) => {
const greenIdData = change.after.val();
const blueId = context.params.blueId;
const greenId = context.params.greenId;
// Do whatever you want with the data and set it to where ever you want
});
Take a look at this docs, it'll help you set the value back to where you want.
Good luck!

Categories

Resources