map through array nodejs - javascript

I have a stripe function where I am trying to push the images into the array for each product. This is part of the prebuilt stripe checkout page... here's my function at whole:
//defining arrays
var productsArray = [];
var priceArray = [];
var imageArray = [];
//query to database
var productsStripe = "select * from " + tableID + "";
ibmdb.open(db2ConnString, function(err, conn) {
if (err) return console.log(err);
conn.query(productsStripe, async function (err, rows) {
if (err) {
console.log(err)
}
console.log(rows)
var itemName = ""
var itemPrice = ""
var totalNewPriceTest = ""
for(var i = 0; i < rows.length; i++)
//inserting items and prices into arrays
productsArray.push(rows[i]['NAME'])
priceArray.push(rows[i]['PRICE'])
imageArray.push(rows[i]['IMAGE_URL'])
}
//stripe
// loop over products array to construct the line_items
const items = productsArray.map((product, i) => {
return {
price_data: {
currency: 'CAD',
product_data: {
name: product,
images: imageArray[i]
},
unit_amount: parseInt(priceArray[i], 10) * 100,
},
quantity: 1,
};
});
its returning the error: (node:39728) UnhandledPromiseRejectionWarning: Error: Invalid array
how do I get the image array to populate the stripe page with images too? I pushed the values into the array, and am calling them , but not sure why it is returning invalid array. any ideas?

Observations
imageArray is array containing image urls for different products, one for each product in the productArray.
From the name of the prop images, the error message and the error line info provided by the OP, the prop images ought to be an array.
In the code, image: imageArray[i] means that the image prop is assigned a string but it should be array. So changing it with [imageArray[i]], we are assigning an array of length one to image prop containing image url for that product.

Related

How to get rows from javascript array response?

I have skills in one table, user_skills in other table and getting skills against id from skills table in for loop.I have stored query results in javascript array. I want a array with objects in it. I'm getting array in array structure. As you can see in image that i am getting multiple arrays in array.It should be objects in single array.
var userExist = await ctx.app.pool.query("SELECT * FROM USER_SKILLS WHERE user_id = $1",
[`${user_id}`]);
var strArray = userExist.rows[0].skill_array.split(",");
var i;
var skillsArray = [];
for (i = 0; i < strArray.length; i++) {
var findSkill = await ctx.app.pool.query("SELECT skill_name FROM ALL_SKILLS WHERE id = $1",
[`${strArray[i]}`]);
skillsArray.push(findSkill.rows);
}
console.log('skillsArray', skillsArray);
ctx.body = {
status: 200,
error: false,
message: "Skills found",
data: skillsArray
};
Assuming skill_array is in fact an array in postgres, you can combine both those queries into one:
const query = "SELECT skill_name
FROM ALL_SKILLS
WHERE id = ANY((
SELECT skill_array
FROM USER_SKILLS
WHERE user_id = $1
))";
const res = await ctx.app.pool.query(query, [`${user_id}`]);
That will be more performant than doing multiple queries.
Then the reason why you're getting an array with arrays is because skillsArray.push(findSkill.rows) puts the whole rows property, which is an array, into your skillsArray array.
It's not entirely clear to me exactly what is the format of the result you want, but I'll assume it's the actual skill names in an array, something like:
{
message: "Skills found",
data: [
"PHP",
"Node js"
]
}
In which case you could restructure your code to be something like this:
const query = "SELECT STRING_AGG(skill_name, ',') AS skill_names
FROM ALL_SKILLS
WHERE id = ANY((
SELECT skill_array
FROM USER_SKILLS
WHERE user_id = $1
))";
const res = await ctx.app.pool.query(query, [`${user_id}`]);
const skillNames = res.rows[0].skill_names.split(',');
ctx.body = {
status: 200,
error: false,
message: "Skills found",
data: skillNames
};
I've added a STRING_AGG because I like to get postgres results in a single row if possible rather than have pg read multiple rows sequentially, I believe it will be faster. I'm not using ARRAY_AGG because I don't know how the pg module treats arrays, whether it converts them to string or a js array. So I return one field with the skills concatenated with a comma, e.g. "PHP,Node js", then just need to split that one field by the comma to get the desired array.
I believe this is what you want
var userExist = await ctx.app.pool.query("SELECT * FROM USER_SKILLS WHERE user_id = $1",
[`${user_id}`]);
var strArray = userExist.rows[0].skill_array.split(",");
var i;
var skillsArray = [];
for (i = 0; i < strArray.length; i++) {
var findSkill = await ctx.app.pool.query("SELECT skill_name FROM ALL_SKILLS WHERE id = $1",
[`${strArray[i]}`]);
skillsArray.push(findSkill.rows[0]);
}
console.log('skillsArray', skillsArray);
ctx.body = {
status: 200,
error: false,
message: "Skills found",
data: skillsArray
};
But I as i mentioned in my comment, I suggest that you have your database tables/schema in relationships M:M, in your case, where you can just get the data you need in a single query, and it is considered bad practice to do a query in for(...) loop like this. you should use ORM like Sequelize or any other ORM that you prefer and works well with KOA, I hope this help you in achieving this:

Get the actual array of rows from SQL query with sqlite and Expo

I am trying to get the whole result as array of rows from sqlite query results object:
I have tried results.rows.item(i).id and it work just fine but I need to get the whole array of rows instead of only one item, i tried to use rows.array (_number) from Expo documentation but I can't figure out how actually to use .array(_number)
my code snippet:
iris.transaction(tx => {
tx.executeSql(
`select id, date from days`,
[],
(tx, results) => {
for(i=0; i<results.rows.length; i++){
var date = results.rows.item(i).date.split('-');
}
}
)
});
As per the Expo document, the result.rows has an _array which returns all the items as array, you can try like
if (result && result.rows && result.rows._array) {
/* do something with the items */
// result.rows._array holds all the results.
}
Hope this will help!
TypeScript Solution to cast _array properly without using any ;)
tx.executeSql("SELECT * FROM ITEMS", [], (_, { rows }) => {
const r = rows as unknown as { _array: MyItem[] };
result = r._array;
});

Pouchdb join / link documents

I have pouchdb/couchbase data with equipment that has user assigned to them.
Equipment with _id and in the equipment doc there is a checkedOutBy with the user._id as the value. Within the employee object there is user.name. When I get the equipment objects how do I also get the user.name and display with the equipment.
I have searched and read about map/reduce that uses emit and do not grasp the idea. My code that i wrote from what i learned is:
by the way I am also using Angularjs.
field = "eq::"
this.getAllEquip = function(field){
function map(doc) {
if (doc.checkedOutBy !== undefined) {
emit(doc.checkedOutBy, {empName : doc.name});
}
}
var result = database.query(map, {include_docs: true,
attachments: true,
startkey: field,
endkey: field + '\uffff'})
.catch(function (err) {
//error stuff here
});
return result
};
I don't see where the two docs would get together. What am i missing? My result is empty.
The equipment json looks like:
{checkedOutBy: "us::10015", description: "3P Microsoft Surface w/stylus & power cord", equipId: "SUR1501", purchaseDate: "", rCost: 1000, id:"eq::10001"}
Emlpoyee json:
{"firstname":"Joe","gender":"male","lastname":"Blow","status":"active","title":"office","type":"userInfo","_id":"us::10015","_rev":"2-95e9f34784094104ad24bbf2894ae786"}
Thank you for your help.
Something like this should work, if I understood the question correctly:
//Sample Array of Objects with Equipment
var arr1=[{checkedout:"abc1",desc:"item1",id:1},
{checkedout:"abc2",desc:"item2",id:2},
{checkedout:"abc3",desc:"item3",id:3},
{checkedout:"abc1",desc:"item1",id:4},
{checkedout:"abc4",desc:"item3",id:5},
{checkedout:"abc6",desc:"item3",id:6}];
//Sample array of objects with Employee - the "id" in arr2 matches with "checkout" in arr1
var arr2=[{name:"john",id:"abc1"},
{name:"jack",id:"abc2"},
{name:"alice",id:"abc3"},
{name:"james",id:"abc4"}];
var result = []; //final result array
//loop through equipment array arr1
arr1.forEach(function(obj) {
var tempObj = obj;
var checkedout_id=obj.checkedout;
//do array.find which will return the first element in the array which satisfies the given function. This is absed on the assumption that that the id is unique for employee and there wont bwe multiple employees with same id (which is the "checkedout" field in equipment. If the employee is not found, it will return undefined.
var foundname = arr2.find(function(obj) {
if (obj.id == checkedout_id)
return obj.name
})
//Create the object to be inserted into the final array by adding a new key called "name", based on the result of above find function
if (foundname != undefined) {
tempObj.name=foundname.name
}
else {
tempObj.name = "Not found";
}
result.push(tempObj);
})
This is my Pouchdb solution, thank you Vijay for leading me to this solution.
First I get all my equipment. Then I use Vijay's idea to loop through the array and add the name to the object and build new array. I found there is a need to go into the .doc. part of the object as in obj.doc.checkedOutBy and tempObj.doc.name to get the job done.
$pouchDB.getAllDocs('eq::').then(function(udata){
var result = [];
//loop through equipment array
udata.rows.forEach(function(obj) {
var tempObj = obj;
var checkedout_id=obj.doc.checkedOutBy;
if (checkedout_id != undefined) {
$pouchDB.get(checkedout_id).then(function(emp){
return emp.firstname + " " + emp.lastname
}).then(function(name){
tempObj.doc.name = name;
});
}
result.push(tempObj);
})
in my service I have:
this.get = function(documentId) {
return database.get(documentId);
};
and:
this.getAllDocs = function(field){
return database.allDocs({
include_docs: true,
attachments: true,
startkey: field,
endkey: field + '\uffff'});
};

Javascript and Mongoose for-loop completes before the .find inside is performed [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Loop with asynchronous callbacks in mongoose/mongodb/node
(1 answer)
Closed 5 years ago.
Me and my partner need to get the average rating per movie. Movie and Rating are two separate collections on our MongoDB database.
So, first we need to get all the movies. After that, all we should still need is to iterate in a for-loop through the length of the list of movies returned. In every iteration we Rating.find on the tt_number of the movie that is currently on the index of the for-loop. At that point, it should calculate an average of all of that movie's ratings and store the result in a string JSON. This is done for every movie.
However, the code I included down here does nothing like that. Instead, the for-loop first completes, and then it performs the Rating.find three times afterwards, except with the tt_number of the last iterated movie every time.
var express = require('express');
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/Notflix', {useMongoClient: true});
var Movie = require('../model/movies.js');
var Rating = require('../model/ratings.js');
var jwt = require('jsonwebtoken');
var router = express.Router();
router.get('/', function (req, res) {
var jsonString = '{"The average ratings":[]}';
var obj = JSON.parse(jsonString);
var moviett_number;
Movie.find({}, function (err, movies) {
if (err) {
res.status(500);
res.json({errorMessage: '500 SERVER-SIDE ERROR - No list of movies to get the average ratings of could be found.'});
}
for (var i = 0; i < movies.length; i++) {
//TODO: I THINK THIS FOR LOOP IS COMPLETED FULLY BEFORE FINALLY THE RATING.FIND IS DONE???????
//Go through the list of all movies, getting the ratings for each...
moviett_number = movies[i].tt_number;
console.log(i + " - " + moviett_number);
Rating.find({'tt_number': moviett_number}, function (err, movieRatings) {
//Get all the ratings for the current movie...
if (err) {
res.status(500);
res.json({errorMessage: '500 SERVER-SIDE ERROR - No list of average ratings for this movie could be found.'});
return;
}
if (movieRatings > 0) {
//If it has ratings, calculate the average rating.
var amountOfRatings;
var average = 0;
for (amountOfRatings = 0; amountOfRatings < movieRatings.length; amountOfRatings++) {
average += parseInt(movieRatings[amountOfRatings].rating);
}
average = Math.round((average / amountOfRatings) * 100) / 100;
//Add the average rating for this movie to the response jsonString.
obj["The average ratings"].push({
averageRatingMessage: 'Movie with tt_number = [' + moviett_number + '] has the following average rating.',
averageRating: average
});
} else {
//Add a notice that this movie does not have any ratings and therefore no average rating either to the response jsonString.
obj["The average ratings"].push({noRatingMessage: 'Movie with tt_number = [' + moviett_number + "] has no ratings yet."});
}
//TODO: WATCH FOR THIS CONSOLE.LOG, IT SHOWS A WEIRD ISSUE.
console.log(obj);
});
}
jsonString = JSON.stringify(obj);
//TODO: ASYNCHRONOUS ISSUE, RESPONSE IS SET BEFORE THE CODE ABOVE IS DONE, BECAUSE THE PROGRAM CONTINUES EVEN IF THE FOR LOOP ISN'T DONE YET!
console.log(jsonString);
res.status(200);
res.json(jsonString);
});
});
Output:
0 - 123
1 - 456
2 - 789
{"The average ratings":[]}
{ 'The average ratings': [ { noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' } ] }
{ 'The average ratings':
[ { noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' },
{ noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' } ] }
{ 'The average ratings':
[ { noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' },
{ noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' },
{ noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' } ] }
Update
This question is not a duplicate of the others presented, as this one deals with response builders and dynamic response content. The others are general on how to handle for-loops and tasks to be completed before others. They come close to what I was looking for, but I hadn't found those either while looking for 2 hours straight, plus they just about miss on what I was looking for.
The Model.find() method is asynchronous, so you are sending the result before it is returned from the database. You will need to use a callback once you have fetched all the data to actually send the response.
// callback when all your Rating.find() are complete
function done(movies) {
res.json(movies);
}
// get the movies
Movie.find({}, function (err, movies) {
// loop over the results
movies.forEach(function(movie, i) {
// get the Rating for this Movie
Rating.find({'tt_number': movie.tt_number}, function (err, ratings) {
// add the rating
movie.rating = ratings.reduce(function(total, rating) {
total += rating;
return total;
}, 0) / ratings.length;
// this is the last one, call done()
if (movies.length-1 === i) {
done(movies);
}
});
}
});
That's happening because of how Node.js works. It first executes the for loop and it sees a Rating.find to execute, but since Node.js cant execute 2 things at the same time (and its already busy with the for loop) it adds the Rating.find into the a stack to be executed later (when Node.js is free), that happens 3 times in your case because you have 3 movies. When the for loop is done, now Node.js is free to execute the Rating.find but now the value of i (from the for loop) is already 3, that that's why it will always insert the last rating.
To solve this issue (there are multiple ways) you can solve the Rating.find inside an IIFE (Inmediatly Invoked Function Expression). Check below:
var express = require('express');
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/Notflix', {useMongoClient: true});
var Movie = require('../model/movies.js');
var Rating = require('../model/ratings.js');
var jwt = require('jsonwebtoken');
var router = express.Router();
router.get('/', function (req, res) {
var jsonString = '{"The average ratings":[]}';
var obj = JSON.parse(jsonString);
var moviett_number;
Movie.find({}, function (err, movies) {
if (err) {
res.status(500);
res.json({errorMessage: '500 SERVER-SIDE ERROR - No list of movies to get the average ratings of could be found.'});
}
for (var i = 0; i < movies.length; i++) {
//TODO: I THINK THIS FOR LOOP IS COMPLETED FULLY BEFORE FINALLY THE RATING.FIND IS DONE???????
//Go through the list of all movies, getting the ratings for each...
moviett_number = movies[i].tt_number;
console.log(i + " - " + moviett_number);
(function() {
Rating.find({'tt_number': moviett_number}, function (err, movieRatings) {
//Get all the ratings for the current movie...
if (err) {
res.status(500);
res.json({errorMessage: '500 SERVER-SIDE ERROR - No list of average ratings for this movie could be found.'});
return;
}
if (movieRatings > 0) {
//If it has ratings, calculate the average rating.
var amountOfRatings;
var average = 0;
for (amountOfRatings = 0; amountOfRatings < movieRatings.length; amountOfRatings++) {
average += parseInt(movieRatings[amountOfRatings].rating);
}
average = Math.round((average / amountOfRatings) * 100) / 100;
//Add the average rating for this movie to the response jsonString.
obj["The average ratings"].push({
averageRatingMessage: 'Movie with tt_number = [' + moviett_number + '] has the following average rating.',
averageRating: average
});
} else {
//Add a notice that this movie does not have any ratings and therefore no average rating either to the response jsonString.
obj["The average ratings"].push({noRatingMessage: 'Movie with tt_number = [' + moviett_number + "] has no ratings yet."});
}
//TODO: WATCH FOR THIS CONSOLE.LOG, IT SHOWS A WEIRD ISSUE.
console.log(obj);
});
})();
}
jsonString = JSON.stringify(obj);
//TODO: ASYNCHRONOUS ISSUE, RESPONSE IS SET BEFORE THE CODE ABOVE IS DONE, BECAUSE THE PROGRAM CONTINUES EVEN IF THE FOR LOOP ISN'T DONE YET!
console.log(jsonString);
res.status(200);
res.json(jsonString);
});

Getting Array from Prompt and Inserting into MongoDB Database

I am working on a social network type site. I am wanting a way in which I can prompt users, get their response in an array, and then insert the array all at once. Whilst I can prompt I'm having trouble inserting the array into the mongodb database. Here is my code:
'click #creator': function(event){
var channelName = prompt('Enter The Channel Name');
var howmany = +prompt('How many people do you want? (max 10)');
var users = [];
var arr = []; // define our array
if(howmany > 1 && howmany<10){
for (var i = 0; i < howmany; i++) { // loop 10 times
arr.push(prompt('Enter a user' + (i+1))); // push the value into the array
}
users = arr.join('"," ');
Meteor.call('addChannel', channelName, users);
}
}
Inserting it in:
Channels = new Mongo.Collection('channels');
Meteor.methods({
addChannel: function(channelName, users){
if(!Meteor.userId()) {
throw new Meteor.Error('not-authorized', 'you are not signed in');
}
var username = Meteor.user().username;
Channels.insert({
name: channelName,
created: new Date(),
members: $push: {users}
createdBy: username
});
},
});
Since you are inserting and users is already an array you don't need to $push. Plus you don't need to push an {} object.
Channels.insert({
name: channelName,
created: new Date(),
members: users,
createdBy: username
});
Meanwhile on the client side, skip the arr.join() - that is creating a string but you just want to pass the array you created directly.
Meteor.call('addChannel', channelName, arr);

Categories

Resources