The get method should receive a userid which has a total number of initial points defined in stcok.json file and some transactions defined in another.
sample entries below for the stock json
[
{ "user": "abcdef", "points": 678 },
{ "user": "gfdsert", "points": 8414 }
]
the other file, transaction json file which has many transactions recorded for the userid. If the points(pts) in the transaction is of the type 'plus' it should be deducted from the initial points and if it is 'minus' the points should be added.
[
{ "user": "abcdef", "type": "plus", "pts": 8 },
{ "user": "dfgvhj", "type": "plus", "pts": 4 },
{ "user": "gfdsert", "type": "minus", "pts": 5 },
{ "user": "gfdsert", "type": "plus", "pts": 3 },
{ "user": "lkjhgf", "type": "plus", "pts": 6 }
]
I need help in reading these two files in an efficient way. I am reading this synchronously and I know it is not a good way to read the files. Everything works when entries are present in both the files for a userid
Also I am not able to get this working during the error path. Always i stumble on "undefined" error both for the points array and the transactions array when the entries are not present in the files. If the user is not present in both the files it should throw an error "user not found". However the user can be present in any or both of the files. If the user is not present in the stock json file, the initial points should be treated as zero. Similarly the user may or may not have any transactions.
my code below --- what i tried
let pointlist = JSON.parse(fs.readFileSync("./stock1.json", "utf8"));
let transactionlist = JSON.parse(fs.readFileSync("./transactions1.json", "utf8"));
// Definition of the callback function
const callback = (err, points, positiveTransactions, negativeTransactions) => {
if(err) {
return `user with given id ${err} not found`;
}
else {
let sum = positiveTransactions.reduce(function (previousValue, currentValue) {
return previousValue currentValue.pts
}, 0)
let diff = negativeTransactions.reduce(function (previousValue, currentValue) {
return previousValue currentValue.pts
}, 0)
let value = points.points - sum diff
return "Here is the number of remaining points: " value;
}
}
// Passing userId and callback function as parameter
const findPoints = (userId, callbackFunction) => {
let points = pointlist.find(function(userValue) {
return userValue.user == userId;
});
let positiveTransactions = transactionlist.filter(function(userValue) {return userValue.user == userId && userValue.type == 'plus' });
let negativeTransactions = transactionlist.filter(function(userValue) {return userValue.user == userId && userValue.type == 'minus' });
// user not found
if(typeof points === 'undefined' && typeof positiveTransactions === 'undefined' && typeof negativeTransactions === 'undefined') {
return callbackFunction(userId, false, false, false);
}
else { // user found
return callbackFunction(null, points, positiveTransactions, negativeTransactions);
}
}
const getPoints = (req, res)=>{
// Sending back the response to the server
res.status(200).send(findPoints(req.query.userId, callback));
}
app.get('/getName', getPoints);
app.listen(8000, 'localhost', function () {
console.log('Server Listening');
});
CodePudding user response:
I couldn't quite figure out what you were doing wrong, so I refactored to make it more clear and get it working. I also tried to add comments to explain my thinking. Play with this a bit and feel free to ask any questions you might have about it. Also I didn't change the way you're reading the files. IMO readFileSync
is fine if you can be confident that you're not going to run out of memory (which should only happen if your list is very large). At that point, I would look into reading the file as a stream instead.
const express = require('express')
const app = express()
// Read files here. I'm skipping that part.
const stock = [
{ "user": "mike", "points": 678 },
{ "user": "john", "points": 8414 }
]
const transactions = [
{ "user": "mike", "type": "plus", "pts": 8 },
{ "user": "joe", "type": "plus", "pts": 4 },
{ "user": "john", "type": "minus", "pts": 5 },
{ "user": "john", "type": "plus", "pts": 3 },
{ "user": "luke", "type": "plus", "pts": 6 }
]
// Better to name your endpoints after nouns, and leave the verb out of it since it's redundant (app.get("/GETpoints"))
app.get('/points', (req, res) => {
const userId = req.query.userId
if (!req.query.userId) {
return res.status(404).json({ message: 'userId query param must be provided' })
}
// Make sure both of these are arrays since we will call array methods on them later
// Note: 500 means "Internal Server Error" and it's common to send JSON instead of plain strings
if (!Array.isArray(stock) || !Array.isArray(transactions)) {
return res.status(500).json({ message: 'Error in file data. Expected arrays. Check stock and transactions files.' })
}
// Find the matching user in the stock file
const stockUser = stock.find(userObject => {
return userObject.user === userId
})
// Find the user's transactions
const userTransactions = transactions.filter(transaction => {
return transaction.user === userId
})
// Handle the case where user is missing from both files
if (!stockUser && userTransactions.length === 0) {
return res.status(404).json({ message: 'User not found' })
}
// Default to 0 if stockUser.points is falsy (null, undefined, false, 0, empty string)
// I'm using something called "optional chaining" here to make sure stockUser.points safely fails if
// stockUser is null or undefined. You can read more about it, it's very useful.
const startingPoints = stockUser?.points || 0
let points = startingPoints
// I used simple if/else instead of reduce as a personal preference since I find it more readable
userTransactions.forEach(transaction => {
if (transaction.type === 'plus') {
points = transaction.pts
} else if (transaction.type === 'minus') {
points -= transaction.pts
}
})
return res.status(200).json({userId: userId, points: points})
});
app.listen(8000, () => {
console.log('Server Listening');
});