I am just beginning with JS and am having trouble with scope and executing code in similar style as I would with Python. I have started learning JS on Codecademy and have just begun my first project.
My code for the project is below:
//////////////////////////
// R U Hungry Console App
/////////////////////////
// Step 1: Load in Necessary Modules
////////////////////////////////////
// add in the prompt-sync module
// allows to take in and display users name
const prompt = require('prompt-sync')();
// load in fs module
// allows reading in from text files
const fs = require("fs");
// load open module
//allows the opening of webpages
const open = require('open');
// Step 2: Create a menu object
///////////////////////////////
const menu = {
starters: [],
mains: [],
desserts: []
}
// Step 3 Create a factory function to update the Menu object
/////////////////////////////////////////////////////////////
const menuUpdate = (course,dishName,dishLink) => {
if (course.toLowerCase() === 'starter'){
let newItem = {dish: dishName, link: dishLink};
menu.starters.push(newItem);
} else if (course.toLowerCase() === 'main'){
let newItem = {dish: dishName, link: dishLink};
menu.mains.push(newItem);
} else if (course.toLowerCase() === 'dessert'){
let newItem = {dish: dishName, link: dishLink};
menu.desserts.push(newItem);
} else {
console.log('You did not enter a valid course.\nCould not update menu');
}
}
// Step 4: Read in text files of scraped web data
/////////////////////////////////////////////////
const dishes = [menu.starters,menu.mains,menu.desserts];
const filesToRead = ['starters.txt','mains.txt','desserts.txt'];
function addFiles(course,file){
const text = fs.readFileSync(`./menu_files/${file}`);
const textByLine = text.toString().split("\n");
for (const line of textByLine){
course.push(line);
}
}
addFiles(dishes[0],filesToRead[0]);
addFiles(dishes[1],filesToRead[1]);
addFiles(dishes[2],filesToRead[2]);
// Step 5: Put it all together
//////////////////////////////
console.log('\n\nFeeling hungry and can\'t decide what to eat? You have come to the right place.')
const name = prompt('What is your name? ');
console.log(`\nWelcome, ${name}!\nWould you like to be:\n1.Presented With a Menu\n2.Add a Dish to the Menu`);
let userChoice;
while (true){
userChoice = prompt('\nEnter 1 to get a Menu\nEnter 2 to add a Menu Item\nEnter 3 to exit R U Hungry ');
if (userChoice.trim() === 1){
const starterSelector = Math.floor(Math.random() * menu.starters.length);
const mainSelector = Math.floor(Math.random() * menu.mains.length);
const dessertSelector = Math.floor(Math.random() * menu.desserts.length);
let starterDish = menu.starters[starterSelector][0];
let starterRecipe = menu.starters[starterSelector][1];
let mainDish = menu.mains[mainsSelector][0];
let mainRecipe = menu.mains[mainsSelector][1];
let dessertDish = menu.desserts[dessertSelector][0];
let dessertRecipe = menu.desserts[dessertSelector][1];
console.log(`${name}, your Menu is as follows:\n`);
console.log(`Starter: ${starterDish}`);
console.log(`Main: ${mainDish}`);
console.log(`Dessert: ${dessertDish}`);
console.log('\nWe will direct you to recipes for your selected dishes');
// opens the url in the default browser
open(starterRecipe);
open(mainRecipe);
open(dessertRecipe);
} else if (userChoice.trim() === 2){
let userCourse = prompt('Is your dish a Starter, Main or Dessert? ');
let userDishName = prompt('Great! Please tell me the name of your dish ');
let userDishLink = prompt('Please provide the link to the dish recipe ');
menuUpdate = (userCourse,userDishName,userDishLink);
console.log('Menu updated with your dish!');
} else {
console.log(`Goodbye, ${name}.`);
break;
}
console.log('Would you like to perform another function?');
}
// End
I am having trouble with the while loop at the end.
This part specifically:
let userChoice;
while (true){
userChoice = prompt('\nEnter 1 to get a Menu\nEnter 2 to add a Menu Item\nEnter 3 to exit R U Hungry ');
if (userChoice.trim() === 1){
const starterSelector = Math.floor(Math.random() * menu.starters.length);
const mainSelector = Math.floor(Math.random() * menu.mains.length);
const dessertSelector = Math.floor(Math.random() * menu.desserts.length);
let starterDish = menu.starters[starterSelector][0];
let starterRecipe = menu.starters[starterSelector][1];
let mainDish = menu.mains[mainsSelector][0];
let mainRecipe = menu.mains[mainsSelector][1];
let dessertDish = menu.desserts[dessertSelector][0];
let dessertRecipe = menu.desserts[dessertSelector][1];
console.log(`${name}, your Menu is as follows:\n`);
console.log(`Starter: ${starterDish}`);
console.log(`Main: ${mainDish}`);
console.log(`Dessert: ${dessertDish}`);
console.log('\nWe will direct you to recipes for your selected dishes');
// opens the url in the default browser
open(starterRecipe);
open(mainRecipe);
open(dessertRecipe);
} else if (userChoice.trim() === 2){
let userCourse = prompt('Is your dish a Starter, Main or Dessert? ');
let userDishName = prompt('Great! Please tell me the name of your dish ');
let userDishLink = prompt('Please provide the link to the dish recipe ');
menuUpdate = (userCourse,userDishName,userDishLink);
console.log('Menu updated with your dish!');
} else {
console.log(`Goodbye, ${name}.`);
break;
}
console.log('Would you like to perform another function?');
}
It keeps executing the code in the else block and then exiting the program.
In python I would have used something like this:
while (True):
choice = input("What is your name? ")
if choice.strip().lower() != 'john':
print("Who are you?")
break;
elif choice choice.strip().lower() != 'shaun':
print("Who are you?")
break;
else:
print("Hi there, glad you aren't John or Shaun")
continue
Stupid example but I just wanted to show how I could normally have achieved something like this before.
Would anyone be able to explain what is incorrect?
I also struggle to understand the scope in JS. Is that perhaps the problem here?
I am finding it difficult in some cases to apply my thinking from Python to JS.
Any help would be appreciated. I am really wanting to learn.
Thanks!
Maybe as a starter you can you == rather than === as it would not match the type, also in your else if it seems you are calling function incorrectly, remove =.
Related
I have a command file for a discord bot that contains the command and a piece of parsing logic contained within a function that I want to reuse within my index.js
// file: ./commands/scrumPrompt.js
// The function
const extractDeets = function (f, scrum) {
let items = [];
let re = new RegExp("(\n[ -]*" + f + ".*)", "g");
let replace = new RegExp("[ -]*" + f + "[ ]+");
for (const item of scrum.matchAll(re)) {
items.push(item[1].trim().replace(replace, ""));
}
return items;
};
// The actual command itself within the same file
module.exports = {
name: "scrum",
usage: `!scrum < followed by your message > as per Standup format - refer !show for showing the format`,
description: "Reply to standup prompt",
async execute(message, args) {
if (message.channel.type === "text") {
if (!args.length)
return message.reply(
"Please Provide your scrum as per the format in help menu !scrum < your message >"
);
else {
if (message.author.id !== -1) {
const client = new MongoClient(MONGO_URI);
try {
const database = client.db(DB_NAME);
const members = database.collection("members");
const query = { user_id: message.author.id };
const membersdetail = await members.findOne(query);
if (membersdetail !== null) {
// since this method returns the matched document, not a cursor, print it directly
//console.log("Adding Scrum for ", membersdetail.email);
let userscrum = args.splice(0).join(" ");
// Check if multiple !scrum commands are present in developer scrum message
if (userscrum.includes("!scrum") == false) {
// Expects notations of "-" to exist
let [f, e, b, o, bl] = ["f", "e", "b", "o", "x"];
let features = extractDeets(f, userscrum);
let enhancements = extractDeets(e, userscrum);
let bugs = extractDeets(b, userscrum);
let others = extractDeets(o, userscrum);
let blockers = extractDeets(bl, userscrum);
.
.
.
};
I want to keep the name of the function as extractDeets() itself so that it doesn't mess with the usage within the command as well. I'm not completely sure how to export it into the index.js because it's already kind of being imported here:
// Imports the command file + adds the command to the bot commands collection
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
bot.commands.set(command.name, command);
}
I'm unsure of how to add the function as another import, maybe I should export it into another file and then import it from there? I'm not sure if that's possible or doable here. I've tried directly importing from here but then the command doesn't work, which is troublesome.
You can do it like this:
module.exports = { extractDeets };
Later, you can import it like this:
const { extractDeets } = require('../your_file');
I'm trying to refactor the following code, which depending on the top level domain of the recipients email, changes where you click through to in my button. I currently do this with two consts, which I need to refactor into only one.
const CCENTERURL_AT= `${HOSTURL_AT}/ccenter/zendesk/landing/`;
const CCENTERURL = `${HOSTURL}.com/ccenter/zendesk/landing/`;
const recipientEmail = data.ticket.recipient;
var cCenterUrl;
if(recipientEmail.indexOf(".com") > 0)
{
cCenterUrl = getCcenterUrl(zendeskID)
}else{
cCenterUrl = getAustrianCcenterUrl(zendeskID)
}
function getCcenterUrl(ticketID) {
const cCenterTicketUrl = CCENTERURL + ticketID ;
return cCenterTicketUrl;
}
// Get Austrian Ccenter Ticket Url using Zendesk ticket ID
function getAustrianCcenterUrl(ticketID) {
const cCenterTicketUrlAustria = CCENTERURL_AT + ticketID ;
return cCenterTicketUrlAustria;
}
I know I should be able to create a function which will take recipient Email`s top-level domain as parameter and return appropriate URL for CCENTERURL. But no matter what I've tried its become overcomplicated or hasn't worked. I would be interested to hear peoples opinions on either how I can achieve my goal or even how it would be better to go about this!
So you basically want to combine these into one function and use a string template like this.
const HOSTURL = 'example.com/'
const HOSTURL_AT = 'example.au/'
const reAu = /\.au$/;
const getTicketURL = (
(mail, id) => `${ reAu.test(mail) > 0 ? HOSTURL : HOSTURL_AT }ccenter/zendesk/landing/${id}`
);
// Test AU
console.log(getTicketURL('foo#google.com.au', 'ABC1231'))
// Test US
console.log(getTicketURL('foo#google.com', 'ABC1231'))
I have a simple program that reads a css file (which is available on GitHub pages) and parse the css to an array so I can put at in a list.
I have an dictionary with words, like a vocabulary list which shows me the translation and I can also search for words.
Everything works fine so far but I am really new and this is my first project and I want some advise about my codestructure.
At the moment I have to fetch the csv file (which never changes) in different methods with getData. I do this in init dictionary and again in search and show, because I need the dictionary again. Wouldn’t it be better to get the data once and then use it with all functions?
But then I have to write a lot function into another which is not so good practice I think.
I would really appreciate some advise or link or topics/ direction I have to research, because I can’t find an answer about how to structure this well.
I just use html, css and js without any framework or library.
initDictionary();
async function initDictionary(){
//get data
const dictionary = await getData();
//fill html list with words
fillList(dictionary);
}
//show translation and info when click on word
show = async function (i){
let dictionary = await getData();
document.getElementById("word-text").innerHTML = dictionary[i].Wort;
document.getElementById("info").innerHTML = dictionary[i].Notizen;
}
//search stuff
search = async function() {
let dictionary = await getData();
let query = document.getElementById('search').value;
console.log(query);
if (query == ""){
return;
}
//init found to false
let found = -1;
for(let i=0; i< dictionary.length; i++){
if(query == dictionary[i].Übersetzung){
found = i;
break;
}else {
document.getElementById("word-text").innerHTML = "word not found";
document.getElementById("info").innerHTML ="";
}
}
if ( found >= 0){
show (found);
query = document.getElementById('search').value="";
}
}
//Start search when u press enter
let input = document.getElementById("search");
input.addEventListener("keyup", function(event) {
console.log(event);
if (event.key=== "Enter") {
search();
}
});
// get data
async function getData(){
const csv = await fetch('https://aboutwhite.github.io/data/data.csv');
let scvText = await csv.text();
let dictionary = csvToArray(scvText);
return dictionary;
}
// fill html list with words
function fillList(dictionary){
for(let i=0; i< dictionary.length; i++){
document.getElementById('word-list').innerHTML += "<li onclick='show("+i+")'>" + dictionary[i].Übersetzung+"</li>";
}
}
//parse csv to array
function csvToArray(str){
let delimiter = ","
const headers = str.slice(0, str.indexOf("\n")).split(delimiter);
const rows = str.slice(str.indexOf("\n") + 1).split("\n");
const arr = rows.map(function (row) {
const values = row.split(delimiter);
const el = headers.reduce(function (object, header, index) {
object[header] = values[index];
return object;
}, {});
return el;
});
return arr;
}
I've been developing a scraper-type chrome extension for internal/personal use to scrape course data from a university's website.
The high-level algorithm is as follows:
Open up the main page where the user can input the class data they want to find. (The point is to use the data in this page to generate every possible url for every unique course page)
Generate the first endpoint and open a new tab with that url. (This is what I'm calling the "second degree scrape")
Begin the second degree scrape and when it's done, set the chrome.storage.local to true. Then close the tab.
The content script from the main page reads the local storage and sees that the state is true so it resolves the promise. It resets the local storage to false.
It generates the new url and recursively repeats the process until every possible url is created.
The extension works well when I set the storage true and never modify it and simply console.log every possible url. The error arrises when I let the program open up a new tab and let it update local.storage. Before using local.storage I tried a similar implementation using messaging (simple and long-lived) and background but I had the same issue then.
Any ideas of what I could try?
Here's my code:
background/index.ts
chrome.storage.local.set({ secondDegreeState: false });
content/index.ts
const un:string = "***";
const pw:string = "***"
const levels:Array<string> = ['L', 'U', 'G'];
let ccyys:string = `20212`;
let search_type_main:string = `FIELD`
let signInSequence:Function = () => {
if(document.getElementById("login-button")){
let signInButton:HTMLInputElement = document.getElementById("login-button")?.children[0] as HTMLInputElement;
let username: HTMLInputElement = document.getElementById("username") as HTMLInputElement;
let password: HTMLInputElement = document.getElementById("password") as HTMLInputElement;
username.value = un;
password.value = pw;
signInButton.value = "Begin Scraping";
setTimeout(() => {
signInButton.click();
console.log('Sign in button pressed');
}, 2000);
}
}
let scrapingSeqence:Function = () => {
if(window.location.href === "https://utdirect.utexas.edu/apps/registrar/course_schedule/20212/"){ // If we are in the main registration page
firstDegreeScrape(0, 1);
}
if(window.location.hostname == "utdirect.utexas.edu"){ // Make sure that we're on a proper hostname
secondDegreeScrape();
}
}
function secondDegreePromise(url:string) : Promise<any> {
/// Open up a new tab with the generated URL
window.open(url, '_blank');
return new Promise (function callback(resolve:Function, reject:Function) {
chrome.storage.local.get(['secondDegreeState'], (response) => {
if(chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
reject("Chrome error");
}else if (response.secondDegreeState === false){ // If the second degree state is still not done
console.log('Still waiting for 2nd degree scrape to finish...'+' Its state is '+response.secondDegreeState);
setTimeout(callback, 5000); // repeat promise after n-seconds until state is true.
}else if(response.secondDegreeState === true){ // If the promise is complete
resolve("2nd degree scrape was complete!");
}else {
reject("Oopsie...");
}
})
});
}
// Two base cases, 1: it reaches the end of the levels array, 2: it reaches the end of the FOS list.
let firstDegreeScrape:Function = (levelNum: number, fosNum: number) => {
// Reset the scrape state (Turns it false)
chrome.storage.local.set({ secondDegreeState: false });
if (levelNum < levels.length){ // If not base case #1
const fosParent:HTMLElement|null = document.getElementById("fos_fl"); // Define the FOS parent element.
if(fosParent){ // If the fosParent is present. (Will most likely return true... just for extra safety)
let fosChildren = fosParent.children;
if(fosNum < fosChildren.length){ // If not base case #2
let fos:HTMLOptionElement = fosChildren[fosNum] as HTMLOptionElement; // The individual field of study.
let fosValue:string = fos.value.split(' ').join('+'); // Format FOS
const url:string = `https://utdirect.utexas.edu/apps/registrar/course_schedule/20212/results/?ccyys=${ccyys}&search_type_main=${search_type_main}&fos_fl=${fosValue}&level=${levels[levelNum]}`;
secondDegreePromise(url)
.then((res)=>{ // If the second degree scrape promise is resolved
console.log(res+"Now moving along to next URL.");
firstDegreeScrape(levelNum, fosNum+1); // Generate the next URL and scrape it
})
.catch(res=>{console.log(res)});
}else {
firstDegreeScrape(levelNum+1, 1);
}
}
}
}
let secondDegreeScrape:Function = () => {
// make sure that there is something to scrape
let table: HTMLTableElement = document.getElementsByClassName('rwd-table')[0] as HTMLTableElement;
if(table){
let t_rows:HTMLCollection = table.children[1].children as HTMLCollection;
let t_rows_arr:Element[] = Array.from(t_rows);
for(let i=0; i < t_rows_arr.length; i++){
// console.log(t_rows_arr[i].childElementCount);
if(t_rows_arr[i].childElementCount == 1){ // If the row is a title
let course_title:any = t_rows_arr[i].childNodes[1].firstChild?.firstChild?.textContent;
let divisionRegex = /^[a-z\s]{0,3}/gi;
let courseNumRegex = /\d*\w/m;
console.log("Division: "+course_title.match(divisionRegex)[0]);
course_title = course_title.replace(divisionRegex, "");
console.log("Course Number: "+course_title.match(courseNumRegex)[0]);
course_title = course_title.replace(courseNumRegex, "");
console.log("Course Name: "+course_title);
}else { // If it's a sub-column
let row = t_rows_arr[i];
let rowChildren = row.childNodes;
let unique = rowChildren[1].childNodes[0].childNodes[0].textContent; //
console.log("Unique: "+unique);
let days = rowChildren[3].textContent;
console.log("Days: "+days);
let hour = rowChildren[5].textContent;
console.log("Hour: "+hour);
// let room;
let instruction_mode = rowChildren[9].textContent;
console.log("Instruction Mode: "+instruction_mode);
let instructor = rowChildren[11].textContent;
console.log("Instructor: "+instructor);
let status = rowChildren[13].textContent;
console.log("Status: "+status);
let flags = rowChildren[15].textContent;
console.log("Flags: "+flags);
let core = rowChildren[17].textContent;
console.log("Core: "+core);
console.log("\n");
}
}
if(document.getElementById("next_nav_link")){ // If there is a next page
setTimeout(()=>{
document.getElementById("next_nav_link")?.click(); // Click the next button
}, 5000)
}else {
setTimeout(()=>{
// Let's complete the 2nd degree scrape (Sets true) & update the local variable
chrome.storage.local.set({ secondDegreeState: true });
//close the tab
window.close();
}, 1000)
}
}
}
let main:Function = () => {
signInSequence();
scrapingSeqence();
}
main();
manifest.json permissions:
tabs
declarativeContent
storage
activeTab
Thanks for the help!
I have a "clients.txt" file where I have a list of emails. I try to run a program for sending emails where I chose a number of emails to use from the file, in that case the number is 2. After I use the two emails I want to overwrite "clients.txt" without them. The problem is when I try to run the code just for one single time every thing is working! but if I make a loop something is wrong. Looking forward to see any help from you guys. Thanks! I add the code bellow. PS: Sorry for my bad english!
function readEmails(){
const fs = require('fs');
clients_list = fs.readFileSync('clients.txt', 'utf8').split('\n');
let filtered = clients_list.filter(function (el) {
return el != null && el != '';
});
return filtered
}
function dump_array(arr, file){
let fs = require('fs');
let file = fs.createWriteStream(file);
file.on('error', function(err) { /* error handling */ });
arr.forEach(function(v) { file.write(v + '\n'); });
file.end();
}
while_var = 0;
while (while_var < 2){
while_var ++;
let all_clients = readEmails();
let selected_clients = [];
if (all_clients.length > 0){
selected_clients = all_clients.splice(0,2);
dump_array(all_clients, 'clients.txt');
console.log(selected_clients);
}else{
console.log('No more clients')
}
}
const fs = require('fs');
function readEmails(){
const clients_list = fs.readFileSync('clients.txt', 'utf8').split('\n');
const filtered = clients_list
// clear false, 0 and undefined too
.filter(el => !!el)
// remove extra spaces and \r symbols
.map(el => el.trim());
return filtered;
}
function dump_array(arr, file){
// Here you need sync method.
fs.writeFileSync(file, arr.join('\n'));
// And here was 'already declared' error in orginal code
}
let while_var = 0;
while (while_var++ < 2){
let all_clients = readEmails();
let selected_clients = [];
if (all_clients.length > 0){
selected_clients = all_clients.splice(0,2);
dump_array(all_clients, 'clients.txt');
console.log(selected_clients);
}else{
console.log('No more clients')
}
}