foreach loop not iterating over string array - javascript

I'm trying to use a for each loop to check if a user's ID is in a blacklist group I created. When I try to iterate over the string array of userID's, it says blacklisted.forEach is not a function. Why is that?
query = { messageTrackingId: req.query.messageId };
messageId = true;
Messages.find(query)
.populate("creator", "username")
.then(documents => {
console.log("documents is");
console.log(documents[0].creatorId);
let otherUser;
if (documents[0].creatorId === req.query.creatorId) {
console.log("ITS A MATCH!")
otherUser = documents[0].recipientId;
}
else if (documents[0].recipientId === req.query.creatorId) {
console.log("ITS not a match!")
otherUser = documents[0].creatorId;
}
let blacklisted = false;
User.find({ _id: otherUser }).select("blacklistGroup").then((res) => {
blacklisted = res[0].blacklistGroup;
console.log("BLACKLIST SERVER RESPONSE");
console.log(blacklisted);
blacklisted.forEach(function(entry) {
console.log(entry);
});
CONSOLE OUTPUT
documents is
5e52cca7180a7605ac94648f
ITS not a match!
BLACKLIST SERVER RESPONSE
[ '5e52e8af484eba456ca9e814',
'5e52f2cc673de71f60019c76',
'5e52f316673de71f60019c77' ]
(node:12992) UnhandledPromiseRejectionWarning: TypeError: blacklisted.forEach is not a function

I'm not sure why it would display as an array if it's an object, but have you tried creating a new array from it and iterating over that? For example:
blacklisted = [...res[0].blacklistGroup];
blacklisted.forEach(function(entry) {
console.log(entry);
});

Is you "User" statement acting like a fetch? If so, you may have to convert your response to json before using it. Something like...
User.find({ _id: otherUser }).select("blacklistGroup")
.then(res => res.json())
.then(json => {
blacklisted = json.blacklistGroup;
console.log("BLACKLIST SERVER RESPONSE");
console.log(blacklisted);
});
});

I realized that for some reason when I console.log(res[0].blacklistGroup)
it was returning
[ '5e52e8af484eba456ca9e814',
'5e52f2cc673de71f60019c76',
'5e52f316673de71f60019c77' ]
which is also return type Object. So to solve this, I did the following:
let MyBlacklist = JSON.stringify(res[0].blacklistGroup);
MyBlacklist = JSON.parse(MyBlacklist);
then I was able to loop through
let counter = 0;
for (let i = 0; i < MyBlacklist.length; i++) {
counter++;
console.log(MyBlacklist[i]);
console.log(counter);
}
OUTPUT:
5e52e8af484eba456ca9e814
1
5e52f2cc673de71f60019c76
2
5e52f316673de71f60019c77
3

Related

Trouble with Addition Assignment in Array from Firebase

I have a scenario where i need to query multiple collections at once and retrieve the values based on the collection name. I use Promise.all to do so and it works accordingly like so
var dbPromises = [];
dbPromises.push(
admin.firestore().collection("collection1").where("user_id", "==", uid).get(),
admin.firestore().collection("collection2").where("user_id", "==", uid).get(),
admin.firestore().collection("collection3").where("user_id", "==", uid).get(),
);
const promiseConst = await Promise.all(dbPromises);
promiseConst.forEach((qs) => {
if (qs.size > 0) {
if (qs.query._queryOptions.collectionId == "collection1") {
qs.docs.map((doc) => {
valuesArr1.push(doc.data().arr);
});
} else if (qs.query._queryOptions.collectionId == "Collection2") {
qs.docs.map((doc) => {
valuesArr2.push(doc.data());
});
} else if (qs.query._queryOptions.collectionId == "collection3") {
qs.docs.map((doc) => {
valuesArr3.push(doc.data());
});
}
} else {
return
}
});
for (var i=0; i < valuesArr1.length; i++) {
if (valuesArr1[i].desiredData) {
console.log('datas from for loop on datas array', valuesArr1[i].desiredData)
globalVariable += `<img src="${valuesArr1[i].desiredData}">`;
}
}
Once I do this I map the query snapshot I get and am able to retrieve the values up to this point like so
From the first collection I retrieve an array from a firestore document and then the following collections i just retrieve all documents from the collections. This all 'works' in that when I console.log into the functions console the data shows up exactly as expected. It's only when I want to iterate over the data and assign the results to a global variable to use elsewhere that strange behavior occurs.
The console.log shows the desired data in the functions console with no issues, but the output when I interpolate that data into the html and send it off in nodemailer I get the following result
undefined is always the first in the response when i use the += addition assignment operator, but if i just use the = assignment operator there's no undefined but I obviously don't get all the data I'm expecting.
There are no undefined values or documents in the collections that I'm retrieving, I've checked thoroughly and even deleted documents to make sure of it. After days of researching I've come to the conclusion it has to do with the asynchronous nature of the promise I'm working with and the data not being immediately ready when I iterate it.
Can someone help me understand what I'm doing wrong and how to fix it in node?
I figured out a solution to my problem and would like to share it in hopes it saves a future viewer some time.
Before, I was storing the results of the array from Firebase inside a global variable. To save some head scratching I'll post the code again below.
var globalVariableArray = []
var globalVariable
var dbPromises = [];
dbPromises.push(
admin.firestore().collection("DataCollection").where("user_id", "==", uid).get()
);
const promiseConst = await Promise.all(dbPromises);
promiseConst.forEach((qs) => {
if (qs.size > 0) {
if (qs.query._queryOptions.collectionId == "DataCollection") {
Promise.all(
qs.docs.map(doc => {
globalVariableArray = doc.data().arrayWithDesiredData;
})
);
}
else {
return
}
});
globalVariableArray.map(gv => {
globalVariable += `<p>gv.desiredData</p>` // <--- Right here is where the problem area was
})
var mailOptions = {
from: foo#blurdybloop.com,
to: 'bar#blurdybloop.com
subject: 'Almost but not quite',
html: `${globalVariable}`
};
The above code give the expected output, but the output would always have undefined first before the data showed. This happened no matter how the array from Firebase was iterated over.
After strengthening my Google-Fu, I worked out the following solution
var globalVariableArray = []
var globalVariable
var dbPromises = [];
dbPromises.push(
admin.firestore().collection("DataCollection").where("user_id", "==", uid).get()
);
const promiseConst = await Promise.all(dbPromises);
promiseConst.forEach((qs) => {
if (qs.size > 0) {
if (qs.query._queryOptions.collectionId == "DataCollection") {
Promise.all(
qs.docs.map(doc => {
globalVariableArray = doc.data().arrayWithDesiredData;
})
);
}
else {
return
}
});
var mailOptions = {
from: foo#blurdybloop.com,
to: 'bar#blurdybloop.com
subject: 'It works!!',
html: `${globalVariableArray.map(dataIWantedAllAlong => <p>dataIWantedAllAlong.desiredData</p> )}` <--- Here I simply loop through the array inside the interpolation blocks and voila! no more undefined showing up in the results
};
I perform the loop inside the brackets where I interpolate the dynamic data and am no longer getting that pesky undefined showing up in my emails.
Safe travels and happy coding to you all!

why the order of loop changes by parsing data in sqlite database

an xml document is edited using a function. First, all duplicate files are removed. Then the content is entered into a sql database. For this I run a loop that corresponds to the length of the xml elements (here already converted to json). When I look at the content of the table in the database, I see that the order of the entries does not correspond to the order of the array. So within the database query of my server I printed the run variable i and saw that it starts at 0 but 2,3,1 are appended to the end in this order.
Unfortunately I don't understand at all why this happens and hope that someone can help me here.
function extractquestions(filename){
let questions = [];
let sql_insert = 'INSERT INTO Fragen (Titel, Frage) values (?,?)';
fs.readFile('./files/'+filename, function(err, data){
if(err){
throw err;
}
const xmlDataStr = data;
const options = {
ignoreAttributes: false,
attributeNamePrefix : "attr_"
};
const parser = new XMLParser(options);
const output = parser.parse(xmlDataStr);
for(let i = 0; i<output.quiz.question.length; i++){
//Here the needed Questions are filtered out of the xml (Json) data
if(output.quiz.question[i].attr_type == "coderunner"){
questions.push(output.quiz.question[i]);
}
}
//Delete Duplicates from array
const filteredQuestions = questions.reduce((acc, current) => {
const x = acc.find(item => item.name.text === current.name.text);
if (!x) {
return acc.concat([current]);
} else {
return acc;
}
}, []);
for(let i = 0; i<filteredQuestions.length; i++){
db.run(sql_insert, filteredQuestions[i].name.text, filteredQuestions[i], (err, result)=>{
console.log(i);
if(err){
throw err;
}
});
}
});
}

Wait for all Firebase data query requests before executing code

I am trying to fetch data from different collections in my cloud Firestore database in advance before I process them and apply them to batch, I created two async functions, one to capture the data and another to execute certain code only after all data is collected, I didn't want the code executing and creating errors before the data is fetched when i try to access the matchesObject after the async function to collect data is finished, it keeps saying "it cannot access a property matchStatus of undefined", i thought took care of that with async and await? could anyone shed some light as to why it is undefined one moment
axios.request(options).then(function(response) {
console.log('Total matches count :' + response.data.matches.length);
const data = response.data;
var matchesSnapshot;
var marketsSnapshot;
var tradesSnapshot;
var betsSnapshot;
matchesObject = {};
marketsObject = {};
tradesObject = {};
betsObject = {};
start();
async function checkDatabase() {
matchesSnapshot = await db.collection('matches').get();
matchesSnapshot.forEach(doc => {
matchesObject[doc.id] = doc.data();
console.log('matches object: ' + doc.id.toString())
});
marketsSnapshot = await db.collection('markets').get();
marketsSnapshot.forEach(doc2 => {
marketsObject[doc2.id] = doc2.data();
console.log('markets object: ' + doc2.id.toString())
});
tradesSnapshot = await db.collection('trades').get();
tradesSnapshot.forEach(doc3 => {
tradesObject[doc3.id] = doc3.data();
console.log('trades object: ' + doc3.id.toString())
});
betsSnapshot = await db.collection('bets').get();
betsSnapshot.forEach(doc4 => {
betsObject[doc4.id] = doc4.data();
console.log('bets object: ' + doc4.id.toString())
});
}
async function start() {
await checkDatabase();
// this is the part which is undefined, it keeps saying it cant access property matchStatus of undefined
console.log('here is matches object ' + matchesObject['302283']['matchStatus']);
if (Object.keys(matchesObject).length != 0) {
for (let bets of Object.keys(betsObject)) {
if (matchesObject[betsObject[bets]['tradeMatchId']]['matchStatus'] == 'IN_PLAY' && betsObject[bets]['matched'] == false) {
var sfRef = db.collection('users').doc(betsObject[bets]['user']);
batch11.set(sfRef, {
accountBalance: admin.firestore.FieldValue + parseFloat(betsObject[bets]['stake']),
}, {
merge: true
});
var sfRef = db.collection('bets').doc(bets);
batch12.set(sfRef, {
tradeCancelled: true,
}, {
merge: true
});
}
}
}
});
There are too many smaller issues in the current code to try to debug them one-by-one, so this refactor introduces various tests against your data. It currently won't make any changes to your database and is meant to be a replacement for your start() function.
One of the main differences against your current code is that it doesn't unnecessarily download 4 collections worth of documents (two of them aren't even used in the code you've included).
Steps
First, it will get all the bet documents that have matched == false. From these documents, it will check if they have any syntax errors and report them to the console. For each valid bet document, the ID of it's linked match document will be grabbed so we can then fetch all the match documents we actually need. Then we queue up the changes to the user's balance and the bet's document. Finally we report about any changes to be done and commit them (once you uncomment the line).
Code
Note: fetchDocumentById() is defined in this gist. Its a helper function to allow someCollectionRef.where(FieldPath.documentId(), 'in', arrayOfIds) to take more than 10 IDs at once.
async function applyBalanceChanges() {
const betsCollectionRef = db.collection('bets');
const matchesCollectionRef = db.collection('matches');
const usersCollectionRef = db.collection('users');
const betDataMap = {}; // Record<string, BetData>
await betsCollectionRef
.where('matched', '==', false)
.get()
.then((betsSnapshot) => {
betsSnapshot.forEach(betDoc => {
betDataMap[betDoc.id] = betDoc.data();
});
});
const matchDataMap = {}; // Record<string, MatchData | undefined>
// betIdList contains all IDs that will be processed
const betIdList = Object.keys(betDataMap).filter(betId => {
const betData = betDataMap[betId];
if (!betData) {
console.log(`WARN: Skipped Bet #${betId} because it was falsy (actual value: ${betData})`);
return false;
}
const matchId = betData.tradeMatchId;
if (!matchId) {
console.log(`WARN: Skipped Bet #${betId} because it had a falsy match ID (actual value: ${matchId})`);
return false;
}
if (!betData.user) {
console.log(`WARN: Skipped Bet #${betId} because it had a falsy user ID (actual value: ${userId})`);
return false;
}
const stakeAsNumber = Number(betData.stake); // not using parseFloat as it's too lax
if (isNaN(stakeAsNumber)) {
console.log(`WARN: Skipped Bet #${betId} because it had an invalid stake value (original NaN value: ${betData.stake})`);
return false;
}
matchDataMap[matchId] = undefined; // using undefined because its the result of `doc.data()` when the document doesn't exist
return true;
});
await fetchDocumentsById(
matchesCollectionRef,
Object.keys(matchIdMap),
(matchDoc) => matchDataMap[matchDoc.id] = matchDoc.data()
);
const batch = db.batch();
const queuedUpdates = 0;
betIdList.forEach(betId => {
const betData = betDataMap[betId];
const matchData = matchDataMap[betData.tradeMatchId];
if (matchData === undefined) {
console.log(`WARN: Skipped /bets/${betId}, because it's linked match doesn't exist!`);
continue;
}
if (matchData.matchStatus !== 'IN_PLAY') {
console.log(`INFO: Skipped /bets/${betId}, because it's linked match status is not "IN_PLAY" (actual value: ${matchData.matchStatus})`);
continue;
}
const betRef = betsCollectionRef.doc(betId);
const betUserRef = usersCollectionRef.doc(betData.user);
batch.update(betUserRef, { accountBalance: admin.firestore.FieldValue.increment(Number(betData.stake)) });
batch.update(betRef, { tradeCancelled: true });
queuedUpdates += 2; // for logging
});
console.log(`INFO: Batch currently has ${queuedUpdates} queued`);
// only uncomment when you are ready to make changes
// batch.commit();
}
Usage:
axios.request(options)
.then(function(response) {
const data = response.data;
console.log('INFO: Total matches count from API:' + data.matches.length);
return applyBalanceChanges();
}

Loop Inside function of React Component

(React web app development)
In order to check if the current stock status of products, I use ID of products to loop through json data.
I am trying to retrieve value of "DATAPAYLOAD" by key (id) from json (below). idsent is string passed from another component. But "if (Data.response[i].id === idsent)" this condition always appears to be false because I got "failed" in console.
That would be really helpful if someone could take a look at the following code and give me some sujections, thanks in advance!
onButtonClicked = () => {
const idsent="D56F36C6038DFC8244F"
for (var i = 0; i < Data.response.length; i++) {
if (Data.response[i].id === idsent) {
name = Data.response[i].DATAPAYLOAD;
const word = '<INSTOCKVALUE>INSTOCK</INSTOCKVALUE>';
if (name.includes(word)) {
return true;
}
else {
return false;
}
}
console.log("failed");
}
The following is part of the json data that is requested through fetch get-method.
Data= {
"code": 200,
"response": [
{
"id": "CED62C6F96BD0E21655142F",
"DATAPAYLOAD": "<AVAILABILITY>\n <CODE>200</CODE>\n
<INSTOCKVALUE>OUTOFSTOCK</INSTOCKVALUE>\n</AVAILABILITY>"
},
{
"id": "D56F36C6038DFC8244F",
"DATAPAYLOAD": "<AVAILABILITY>\n <CODE>200</CODE>\n
<INSTOCKVALUE>LESSTHAN10</INSTOCKVALUE>\n</AVAILABILITY>"
},
{
"id": "4536C9E608B563A749",
"DATAPAYLOAD": "<AVAILABILITY>\n <CODE>200</CODE>\n
<INSTOCKVALUE>INSTOCK</INSTOCKVALUE>\n</AVAILABILITY>"
},
{
"id": "3A576872130625CABFADEE68",
"DATAPAYLOAD": "<AVAILABILITY>\n <CODE>200</CODE>\n
<INSTOCKVALUE>INSTOCK</INSTOCKVALUE>\n</AVAILABILITY>"
}
]
}
Thank you again.
You probably wanted console.log("failed"); outside of the for loop like the following (so that it only executes once all the data is processed):
onButtonClicked = () => {
const idsent="D56F36C6038DFC8244F"
for (var i = 0; i < Data.response.length; i++) {
if (Data.response[i].id === idsent) {
name = Data.response[i].DATAPAYLOAD;
const word = '<INSTOCKVALUE>INSTOCK</INSTOCKVALUE>';
if (name.includes(word)) {
return true;
}
else {
return false;
}
}
}
console.log("failed");
When the fetch is successful, You need to read and parse the data using json(). Pleas read this
onButtonClicked = async () => {
const idsent="D56F36C6038DFC8244F"
Data = await Data.json(); // json() will create a promise
for (var i = 0; i < Data.response.length; i++) {
if (Data.response[i].id === idsent) {
name = Data.response[i].DATAPAYLOAD;
const word = '<INSTOCKVALUE>INSTOCK</INSTOCKVALUE>';
if (name.includes(word)) {
return true;
}
else {
return false;
}
}
console.log("failed");
}
The reason you get failed, is because the first time through, the ID does not match the one sent, so it console logs the "failed" message. Then the second time through the for loop it matches the data, and then hits the next if, which checks for the value. Since the value you are searching for is included in the data, it returns true and the for loop is exited. The reason you see the fail log is because you are logging when the id doesn't match and there are 3 records in that array where the id don't match, the first one being one of them.

Search a given discord channel for all messages that satisfies the condition and delete

I was trying to make a code that will check all messages in a channel for messages that contain certain words, and delete them if it does contain them. So something like:
if(msg.content.startsWith(prefix+'clean') {
let check = msg.content.split(prefix+'clean')[1]; // Condition, in this case if it containts a certain string
msg.channel.fetchMessages().then(msgs => { // Get messages to check
let msglog = msgs.array() // Make an array with all the messages fetched
for(var i = 0; i < msglog.size; i++) { // Loop to check all messages in array
if (check in msglog[i]) {
// Code to delete that message
};
};
});
};
I am aware that this will not check the entire channel and it will only check the last 50 messages, but I do not know how to make it check the whole channel so this will do until I find out how to do that.
But what code would delete the message that passes the check? Or any different way I could approach this?
Edit:
It seems I was not clear enough, so let's say a channel has the following conversation:
Person A: Hi, guys!
Person B: Hi
Person C: Bye
Let's say I want to delete all the messages with "Hi" in it through my bot, how should I do this? Note: I do not with to delete a message right after it has been sent, I only want to delete it when I want to do so.
Well, this is how I solved my problem after I realised the 2 week limitation of fetchMessages()
else if(msg.content.startsWith(`${prefix}clean`}) { // Check for command
let check = msg.content.split(`${prefix}clean`)[1] // Defines a check
msg.channel.fetchMessages({ limit: 100 }).then(msgs => { // Fetches the last 100 messages of the channel were the command was given
const msgstodelete = msgs.filter(del => del.content.includes(check)) // Filters the messages according to the check
msg.delete() // Deletes the original message with the command
for (var i = 0; i<Array.from(msgstodelete.keys()).length; i++) {
msg.channel.fetchMessage(Array.from(msgstodelete.keys())[i]).then(deldel => deldel.delete())
} // Loop to delete all messages that passed the filter
})
}
The bulkDelete function delete given messages that are newer than two weeks.
if(msg.content.startsWith(prefix+'clean') {
let check = msg.content.split(prefix+'clean')[1]; // Condition, in this case if it containts a certain string
msg.channel.fetchMessages().then(msgs => { // Get messages to check
let msgDel = msgs.filter(msgss => msgss.content.includes(check)) // Finds all messages with 'check'
msg.channel.bulkDelete(msgDel) // Deletes all messages that got found
});
};
To delete the messages older than 2 weeks, you have to iterate through the messages manually to delete them:
async function deleteReturnLast(chan, option, prevMsg, cond) {
return chan.fetchMessages(option)
.then(async msgs => {
if (msgs.size === 0){
if (cond(prevMsg)) {
prevMsg.delete()
.then(d => console.log('last message deleted: ' + d.content))
.catch(err => console.log('ERR>>', err, prevMsg.content, option.before)); }
return prevMsg;
};
let last = msgs.last();
for (const[id, msg] of msgs) {
let tmp = (id === last.id) ? prevMsg : msg;
if (cond(tmp)) {
tmp.delete()
.then(d => console.log('Message deleted: ' + d.content))
.catch(err => console.log('ERR>>', err));
}
};
return last;
})
.catch(err => console.log('ERR>>', err));
}
function cond(msg) {
return !msg.content.includes('a');
}
client.on('message', async function(msg) {
let chan = msg.channel;
let last = chan.lastMessage;
while (last !== (last = await deleteReturnLast(chan, {limit: 2, before: last.id}, last, cond))){
};
});

Categories

Resources