variable cannot be reached inside foreach loop - javascript

I am trying to build a webhook for dialogue flow but cannot access some variable in code
when i run code
match comes out to be undefined
var findclass = (data, day, time) => {
var match;
data.forEach(entry => {
if (entry.day === day && entry.time === time) {
console.log("found");
match=entry;//this statement has no effect on above var match
//instead it creates a new local variable
}
});
return match;
}
exports.tt = functions.https.onRequest((request, response) => {
let qr = request.body.queryResult;
let day = qr.parameters.day;
let time = parseInt(qr.parameters.time.substring(11, 13));
let data = [
{
day: "monday",
time: 11,
type: "lecture",
batches: ["A", "B1", "B4", "B3", "B2"],
subject: {
name: "Basic Numerical Methods",
name_short: "BNM",
coursecode: "17B1NMA531",
coursecode_short: "17MA531"
},
class: "CS1",
teachers: "SSH",
semester: 5
},
{
day: "monday",
time: 15,
type: "lecture",
batches: ["A6", "A9"],
subject: {
name: "Environmental Science",
name_short: "EVS",
coursecode: "15B11GE301",
coursecode_short: "GE301"
},
class: "CS1",
teachers: "EKT",
semester: 5
}]
var match = findclass(data, day, time);
console.log(JSON.stringify(match));
if (match) {
response.send({
fulfillmentText: `Yes, ${match.subject.name_short || match.subject.name}`
});
} else {
response.send({
fulfillmentText: `No class on ${day} ,${time} hr`
});
}
also vscode code is showing var match is unused if i remove return match statement,this means it is not considering match = entry , but i cannot understand why ?

I don't know the reason but two changes fixed the code
1) Adding "use strict" at the top.
2) defining function as
const findclass = () => {}
instead of var

My guess is that your declaration of:
var match ...
is being hoisted ... see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
In your code you have declared:
var match ...
twice. I think the declaration in the body of the findClass function might want to be changed to a local variable ...
let match;
(This is a guess, will delete this answer on request or if better answer comes along).

My guess is that nothing is actually matching in the loop, and therefore match remains undefined. Have you made sure something is actually matching in the loop?
Also, in VSCode, if you are just assigning a variable, but not using it (e.g., in a return statement), it will show the variable as being unused, so that is expected.

Related

Populating Nested Array from Text File

Been trying to see if there might be an easier way for me to manage a dataset by putting it all into a text file rather than having it in the JS itself (the text file will be several hundred lines long by the end), but I can't seem to get the array to populate the way that I need it to.
In the end, I need an array that'll look like this:
var names = [
{
"name": "john",
"tag": ["tall","blue eyes","ginger","fast"],
},
{
"name": "morgan",
"tag": ["stout","blue eyes","dark"],
},
{
"name": "ryan",
"tag": ["average","brown eyes","fast","strong","perceptive"]
}
]
Populated with all the names and tags from the text file formatted like this (or something like this, if there's a formatting that'll work better):
john: tall ,blue eyes, ginger, fast
morgan: stout, blue eyes, dark
ryan: average, brown eyes, fast, strong, perceptive
Here's where I've gotten myself thus far, searching around here and elsewhere. Mostly struggling with the array of tags. Currently it's spitting it out as a string, but I'm not really sure how to break it down.
const { readFile, promises: fsPromises } = require('fs');
readFile('NAMES.txt', 'utf-8', (err, data) => {
if (err) throw err;
var name = data.split(/\r?\n/), result = [], anotherarray = [];
name.forEach((pair) => {
if (pair !== '') {
let splitpair = pair.split(': ');
let key = splitpair[0].charAt(0).toLowerCase() + splitpair[0].slice(1);
result[key] = splitpair[1];
}
});
for (var i in result) anotherarray.push({ "name": i, "tag": result[i] });
console.log(anotherarray);
});
Any help or pointing in the right direction would be much appreciated!
You could use readline node module to read the file line by line and process it.
I think the most simple way to process is in two steeps:
Split by name and tags by :
Split the tags by , and trim the spaces [recommend trim by split by , so you can handle cases with multiple spaces]
NOTE: I added lowercase too because your example.
Example:
const readline = require("readline");
const fs = require("fs");
// Create the interface to read the file line by line
const file = readline.createInterface({
input: fs.createReadStream('text.txt'),
});
// answer
const names = [];
// Process file line by line
file.addListener("line", (line) => {
// Split name and tags
const [name, tags] = line.split(":");
// Insert the name with parsed tags
names.push({
name,
tag: tags.split(",").map((e) => e.trim(). toLowerCase()),
})
});
// Log answer
file.addListener("close", () => {
console.log(names);
});
the output:
[
{ name: 'john', tag: [ 'tall', 'blue eyes', 'ginger', 'fast' ] },
{ name: 'morgan', tag: [ 'stout', 'blue eyes', 'dark' ] },
{
name: 'ryan',
tag: [ 'average', 'brown eyes', 'fast', 'strong', 'perceptive' ]
}
]

Aggregating data after building multi-level array in javascript

I have a reduce function that is building multiple levels and is working perfectly for me except for one issue
Currently, it's building data based on employee first, then by date, area, and job. I'm getting all of the data at the proper level but I'm now trying to aggregate certain data for a totals section at the date level and it's just listing values rather than aggregating.
Basically, in the line I've notated below, I'd like to create a value called total_scans that simply adds up ALL scans for any orders on that date. In other words, for the record for Miranda on 8/12 I would expect the total_scans at the date level to have 49 as the value. Am I on the right track?
const nest = (rows) =>
rows.reduce(
(a, row) => {
const employee = a[row.employee] || (a[row.employee] = { dates: {} })
const date = employee.dates[row.job_date] || (employee.dates[row.job_date] = { areas: {} })
const order = date.areas[row.area_number] || (date.areas[row.area_number] = { jobs: {} })
const job = order.jobs[row.job] || (order.jobs[row.job] = { hours: '', scans: '', job_date: '' })
job.hours += row.hours
job.scans += row.scans
job.job_date = row.job_date
//this line is my issue
date.total_scans += job.scans
return a
},
{}
);
new Vue({
el: "#app",
props: {
},
data: {
rows: [
{
employee: "Miranda",
job: "123",
hours: "10",
job_date: "08/12/2021",
scans: 37,
area_number: "1234567",
},
{
employee: "Miranda",
job: "167",
hours: "15",
scans: 12,
job_date: "08/12/2021",
area_number: "1234568",
},
{
employee: "Miranda",
job: "184",
hours: "18",
scans: 24,
job_date: "08/13/2021",
area_number: "1234569",
}
],
},
computed: {
numbersByEmployee() {
return nest(this.rows)
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{numbersByEmployee}}
</div>
Your usage of reduce is a little irregular. The idea of reduce is to take an iterable (array) and return a single value, usually something like a String or Number.
Also, you're causing all sorts of side effects in your reducer, by modifying the object and arrays. Since Javascript is pass-by-reference for arrays and objects, those changes you're causing will be reflected in the original object, which is not how Vue prescribes things are done. If you want to modify data, it should be done in a watch, not a computed.
Finally, I believe you're overcomplicating your reduce function. Instead of a long reduce like that, you could simply do the below. Note the initialValue of 0.
const nest = (rows) =>
rows.reduce(
(sum, row) => {
return sum + row['scans'];
},
0
);
Obviously this will count all scans. If you want to only count scans by date, how about save yourself running the reducer across the array, and instead run filter first? Something like
const nest = (rows) =>
rows.filter(({job_date}) => job_date === SomeDate).reduce(...)
The {job_date} is a destructuring assignment. You could also split out a date filtered array into its own computed.

Why is the class creating an object with possibilities property undefined?

I'm trying to check an object (customer) against another object (developers), there are multiple developers so I have an object which houses all of them and I'm iterating through object that holds the developer, when a customer tests positive for the age condition then a test string is pushed onto the gateKeeper. possibilities property and in turn creates a new customer object from the customer class however, when I run gatekeeper.build(prospect, developers) pass it to a variable and console.log that variable it shows the possibilities properly as undefined, but when I console.log from inside the promise it shows it passed, for both developers, meaning it is pushing 'worked' onto the possibilities property but it isn't using that value when It builds the class, why?
edit
This now does as expected, I replaced the promise chain with a callback chain however as I understand it; the promise syntax paired with async await is the most correct way to execute functions when the timing of execution is important. I think I might be missing something as it regards to promises because code running within the promise chain executes but doesn't seem to affect anything outside the promise chain. If this is the case then (in my limited experience lol) I can't see any use case for the promises or the async await syntax, can someone please help me clarify this?
age: 28,
income: 75,
maritalStatus: 'Married',
majorCc: 'Y',
zipCode: 32805,
possibilities: {}
};
var developers = {
westgate: {
name: 'Westgate',
aTop: 68,
aBot: 28,
income: 50,
majorCc: 'Not Required',
maritalStatus: ['Married', 'Co Hab'],
payload: ['Myrtle Beach', 'Orlando', 'Gatlinburg', 'Cocoa Beach']
},
bluegreen: {
name: 'Bluegreen',
aTop: 999,
aBot: 25,
income: 50,
majorCc: 'Not Required',
maritalStatus: ['Married', 'Single Male', 'Co Hab', 'Single Female'],
payload: ['Myrtle Beach', 'Orlando', 'Gatlinburg', 'Cocoa Beach']
},
};
let gateKeeper = {
prospect: {},
settings: {},
possibilities: [],
methods: {
age(callback1) {
if (gateKeeper.prospect.age >= gateKeeper.settings.aBot && gateKeeper.prospect.age <= gateKeeper.settings.aTop) {
callback1();
}
},
income(callback) {
if (gateKeeper.prospect.income >= gateKeeper.settings.income) {
callback();
}
},
createPayload() {
var cache = {};
cache[gateKeeper.settings.name] = gateKeeper.settings.payload;
gateKeeper.possibilities.push(cache);
},
packageMethods() {
gateKeeper.methods.age(() => {
gateKeeper.methods.income(() => {
gateKeeper.methods.createPayload();
});
});
}
},
resources: class customer {
constructor(prospectInput, developerInput) {
this.age = prospectInput.age;
this.income = prospectInput.income;
this.maritalStatus = prospectInput.maritalStatus;
this.majorCc = prospectInput.majorCc;
this.zipCode = prospectInput.zipCode;
this.possibilities = developerInput; //MUST BE A DICTIONARY WITH ARRAY OF LOCATIONS
}
},
build(prospectInput, developersInput) {
var payload;
gateKeeper.prospect = prospectInput;
for (var i in developersInput) {
gateKeeper.settings = developersInput[i];
payload = gateKeeper.prospect;
gateKeeper.methods.packageMethods();
}
return new gateKeeper.resources(gateKeeper.prospect, gateKeeper.possibilities);
}
};
var test = gateKeeper.build(prospect, developers);
console.log(test.possibilities[1].Bluegreen[3]);

Objects with and without quotation marks

Edit: using google apps script, these are objects that are passed back from their functions. When I say logged I mean the result of the return function is logged in GAS.
I have objects that serve as profiles for a larger script, and I was trying to generate a larger profile programmatically.
When called and logged:
[ { name: "a1",
functionName:"functionA",
options:{something:"a1run"}
},
{ name: "a2",
functionName:"functionA",
options:{something:"a2run"}
},
{ name: "a3",
functionName:"functionA",
options:{something:"a3run"}
}
]
Shows up in the log as this:
[{
functionName = functionA,
name = a1,
options = {
something = a1run
}
},
}, {
functionName = functionA,
name = a2,
options = {
something = a2run
}
}, {
functionName = functionA,
name = a3,
options = {
something = a3run
}
}]
you'll note that all of the quotation marks disappeared.
Yet when I call an almost identical function where I generated each part of the object with a for loop (this)
var s1 = "";
for (var i=0; i<5;i++)
{
var newString = '';
newString += '{ name: "a'+i+'",';
newString += 'functionName: "functionA",';
newString += 'options:{something: "a'+i+'run"} },';
s1+= newString;
}//for loop
The logged result of the function is this:
[{
name: "a0",
functionName: "functionA",
options: {
something: "a0run"
}
}, {
name: "a1",
functionName: "functionA",
options: {
something: "a1run"
}
}, {
name: "a2",
functionName: "functionA",
options: {
something: "a2run"
}
}, {
name: "a3",
functionName: "functionA",
options: {
something: "a3run"
}
}, {
name: "a4",
functionName: "functionA",
options: {
something: "a4run"
}
}, ]
This is a problem because the initial formatting does work as a profile, and the second one does not. What aspect of JavaScript objects do I need to understand? I didn't think it would make a difference because this object goes through a JSON.stringify when it is used but I was wrong.
My question isn't just how I change it so that it is processed the same way, but why one is being treated differently from the other.
This is not the correct way of creating a JSON array
you should do something like this and forget about creating a string of JSON array
let output = [];
for (var i = 0; i < 5; i++) {
output.push({
name: "a" + i,
functionName: "functionA",
options: {
something: "a" + i + "run"
}
});
}
console.log(output);
Instead of using Logger or the Google Apps Script built-id debugger to "print" your JSON in order to debug it if you are able to use Stackdriver use it or use the HTML Service to print the JSON object to your web browser console instead.
The above becase the Log view (View > Logs), as you already found, not always print JSON objects correctly.
If you want to use Logger, first you should convert to JSON object to string. In most cases using JSON.stringify(...) will work fine.
References
https://json.org/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON

Discord.js - How do I set a value to an array string?

I've been working on a "BlackJack" command which is a minigame. Now, I'm pretty sure simply adding the different math generators for each embed would work, and if the addition equation equals over 21, it tells you you've lost! I could do all that myself if I knew how to assign each string within the "cards" array a different value.
For example... Ace of Spades = 11
then I'd be able to use maths... randomCard1 + randomCard 2 sorta thing
const { CommandoClient, SQLiteProvider, Command } = require('discord.js-commando');
const { RichEmbed } = require('discord.js');
const client = new CommandoClient({
commandPrefix: 'w!',
unknownCommandResponse: false,
owner: ['254323224089853953', '121222156121014272'],
disableEveryone: true
});
module.exports = class BlackJackCommand extends Command {
constructor(client) {
super(client, {
name: 'blackjack',
group: 'ping',
memberName: 'blackjack',
description: 'Use w!blackjack [bet] to bet on blackjack! Use w!blackjackhelp to find out more!',
examples: ['w!blackjack 20'],
args: [
{
key: 'ping',
prompt: 'How much ping do you want to bet?',
type: 'integer'
}
]
});
}
async run(message, args) {
var responses = Array('Stand','Hit','Double Down')
var cards = Array('Ace of Clubs','2 of Clubs','3 of Clubs','4 of Clubs','5 of Clubs','6 of Clubs','7 of Clubs','8 of Clubs','9 of Clubs','10 of Clubs','Jack of Clubs','Queen of Clubs','King of Clubs','Ace of Diamonds','2 of Diamonds','3 of Diamonds','4 of Diamonds','5 of Diamonds','6 of Diamonds','7 of Diamonds','8 of Diamonds','9 of Diamonds','10 of Diamonds','Jack of Diamonds','Queen of Diamonds','King of Diamonds','Ace of Hearts','2 of Hearts','3 of Hearts','4 of Hearts','5 of Hearts','6 of Hearts','7 of Hearts','8 of Hearts','9 of Hearts','10 of Hearts','Jack of Hearts','Queen of Hearts','King of Hearts','Ace of Spades','2 of Spades','3 of Spades','4 of Spades','5 of Spades','6 of Spades','7 of Spades','8 of Spades','9 of Spades','10 of Spades','Jack of Spades','Queen of Spades','King of Spades');
var joker = ('<:joker:415835828770570240>')
const randomCard1 = cards[Math.floor(Math.random()*cards.length)];
const randomCard2 = cards[Math.floor(Math.random()*cards.length)];
const randomDealer = responses[Math.floor(Math.random()*responses.length)];
const initial = new RichEmbed()
.setTitle(`**${joker} Blackjack! ${joker}**`)
.setAuthor(message.author.tag, message.author.displayAvatarURL)
.setThumbnail('https://pbs.twimg.com/profile_images/1874281601/BlackjackIcon.png')
.addField('**Initial Deal:**', `Your Cards:\n- ${randomCard1}\n- ${randomCard2}`)
.setColor(0xAE0086)
const dealer1 = new RichEmbed()
.setTitle(`**${joker} Blackjack! ${joker}**`)
.setAuthor(message.author.tag, message.author.displayAvatarURL)
.setThumbnail('https://pbs.twimg.com/profile_images/1874281601/BlackjackIcon.png')
.addField('**Initial Deal:**', `Your Cards:\n- ${randomCard1}\n- ${randomCard2}`)
.addField('**Dealer\'s Turn 1:**', `Choice: ${randomDealer}`)
.setColor(0xAE0086)
message.embed(initial);
const filter = message => message.content.includes('stand');
message.reply('Your turn to choose: ``stand`` ``hit`` ``surrender`` ``double down`` ``cancel``')
.then(function(){
message.channel.awaitMessages(response => filter, {
max: 1,
time: 300000000,
errors: ['time'],
})
.then((collected) => {
message.embed(dealer1);
})
.catch(function(){
message.channel.send('You didnt respond in time!');
});
});
}
}
There are at least two approaches you could take. The first thing I note is that you are treating an ace as equal to 11, but the rules allow for either 1 or 11.
Positional
Because the content of this array follows a pattern we can use math to look at the position within the array and determine the value for the card. If index holds our array offset we can do the following to it:
take the modulus of the number by 13, this will separate the suits with values of zero through 12
add one to that to get the numbers 1-13 within each suit
take the minimum of either that number or 10 to turn the face cards into 10s
go back and work with the value if it is 1 and could also be 11
This might look like:
value = Math.min((index % 13) + 1, 10)
which covers all but the last step of handling the two possible values for Ace.
Objects
You can change the way you define cards to:
var cards = [
{value: 1, name: 'Ace of Clubs'},
{value: 2, name: 'Two of Clubs'},
{value: 3, name: 'Three of Clubs'}
];
and access the name of the card as cards[index].name and the value as cards[index].value where index is the offset within the array. Note that the array is declared with just square brackets instead of Array(.

Categories

Resources