Callback function Node JS - javascript

I'm recently diving into nodejs (and using nightmare.js) to parse a website and am having issues with the callback functions and displaying the returned results. I'm trying to call a separate function in another function, but can't seem to return any results. They all return undefined. Any help on this is greatly appreciated.
function getDetails(loadURL, callback){
nightmare.goto(loadURL)
.wait(2000)
.evaluate(function(){
var gigs = [];
$('.hidden-xs .used-vehicle').each(function(){
item = {}
item["year"] = $(this).attr('data-year')
item["make"] = $(this).attr('data-make')
item["model"] = $(this).attr('data-model')
item["body"] = $(this).attr('data-body')
item["color"] = $(this).attr('data-ext-color')
item["trim"] = $(this).attr('data-trim')
item["mileage"] = $(this).attr('data-mileage')
item["transmission"] = $(this).attr('data-transmission')
item["vin"] = $(this).find(".vehicle-overview").attr('id')
item["title"] = $(this).find(".vehicle-overview h2 a").text()
item["link"] = $(this).find(".vehicle-overview h2 a").attr('href')
item["price"] = $(this).find(".vehicle-content .price").text()
gigs.push(item)
})
return gigs
})
.end()
.then(function(result){
var returnString = '';
for(gig in result){
returnString = returnString + result[gig].title + " " + result[gig].link + " " + result[gig].year + " " + result[gig].make + " " + result[gig].model + " " + result[gig].body + " " + result[gig].color + " " + result[gig].trim + " " + result[gig].transmission + " " + result[gig].vin + " " + result[gig].price + "\n"
}
callback(returnString)
})
}
// We will need to get the total amount of pages that we need to parse
function getInventory(sURL, callback){
nightmare.goto(sURL)
.wait(2000)
.evaluate(function(){
totals = [];
items = {}
totalCars = $('.total-found .count').text()
carsOnPage = $('.hidden-xs .used-vehicle').size()
items['carTotal'] = totalCars
items['onPage'] = carsOnPage
var pageCalc = (totalCars / carsOnPage)
items['tPages'] = Math.ceil(pageCalc)
totals.push(items)
return totals
})
.end()
.then(function(result){
var totalCars = '';
var totalPages = '';
for (item in result){
totalPages = result[item].tPages
totalCars = result[item].carTotal
}
counter = 0;
newURL = '';
returnDetails = '';
for (i =0; i < totalPages; i++){
if (i == 0){
newURL = sURL;
} else {
counter = i + 1;
newURL = sURL + "#action=im_ajax_call&perform=get_results&_post_id=5&page=" + counter + "&show_all_filters=false";
}
//console.log(newURL)
getINV = getDetails(newURL, function(returnString){
callback(returnString)
})
returnDetails = returnDetails + getINV
}
callback(returnDetails)
})
}
getInventory(startURL, function(result){
console.log(result)
})

I won't bother telling you that you should not mix callbacks with promises like that. But let's see the problem for now.
Case 1
How about you check for errors too? Maybe your script is throwing errors. I can see you are calling the callback on .then but nothing on .catch. Maybe then is never getting any data.
Case 2
Let's check your functions. You are calling .end every time. Are you creating new Nightmare instance everytime too?
On the getInventory function, you should not call .end. On the getDetails function, you should not call .end. It's ending the nightmare instances and you are losing your data.
Call nightmare.end() after you are done with all of your functions and works. To do this properly you will need to learn more about Promises check case 3 below.
Case 3
Learn how promises works. On the line below, you are never waiting for the function to finish.
getINV = getDetails(newURL, function(returnString){
callback(returnString)
})
You should wait for the promises to finish. Also, make sure nightmare is not trying to browse two links at same time.
So go ahead and learn about Promises and async await stuff.
How would I solve your code?
I would use Promise.all, .map and bunch of other new stuff. Here are some sample code done for you, don't copy paste or run the code directly, try to understand why it's different from your code and what can be the result of it.
const pLimit = require("promise-limit")(2);
function getDetails(loadURL) {
return nightmare
.goto(loadURL)
.wait(2000)
.evaluate(() => {
const gigs = [];
$(".hidden-xs .used-vehicle").each(function() {
item = {};
item["year"] = $(this).attr("data-year");
item["make"] = $(this).attr("data-make");
item["model"] = $(this).attr("data-model");
item["body"] = $(this).attr("data-body");
item["color"] = $(this).attr("data-ext-color");
item["trim"] = $(this).attr("data-trim");
item["mileage"] = $(this).attr("data-mileage");
item["transmission"] = $(this).attr("data-transmission");
item["vin"] = $(this)
.find(".vehicle-overview")
.attr("id");
item["title"] = $(this)
.find(".vehicle-overview h2 a")
.text();
item["link"] = $(this)
.find(".vehicle-overview h2 a")
.attr("href");
item["price"] = $(this)
.find(".vehicle-content .price")
.text();
gigs.push(item);
});
return gigs;
})
.then(result => {
let returnString = "";
for (gig in result) {
returnString =
`${returnString +
result[gig].title} ${result[gig].link} ${result[gig].year} ${result[gig].make} ${result[gig].model} ${result[gig].body} ${result[gig].color} ${result[gig].trim} ${result[gig].transmission} ${result[gig].vin} ${result[gig].price}\n`;
}
return returnString;
})
.catch(error => {
throw new Error(error);
});
}
// We will need to get the total amount of pages that we need to parse
function getInventory(sURL) {
return nightmare
.goto(sURL)
.wait(2000)
.evaluate(() => {
totals = [];
items = {};
totalCars = $(".total-found .count").text();
carsOnPage = $(".hidden-xs .used-vehicle").size();
items["carTotal"] = totalCars;
items["onPage"] = carsOnPage;
const pageCalc = totalCars / carsOnPage;
items["tPages"] = Math.ceil(pageCalc);
totals.push(items);
return totals;
})
.then(result => {
let totalCars = "";
let totalPages = "";
for (item in result) {
totalPages = result[item].tPages;
totalCars = result[item].carTotal;
}
counter = 0;
newURL = "";
urls = [];
returnDetails = [];
for (i = 0; i < totalPages; i++) {
if (i == 0) {
newURL = sURL;
} else {
counter = i + 1;
newURL =
`${sURL}#action=im_ajax_call&perform=get_results&_post_id=5&page=${counter}&show_all_filters=false`;
}
// push to the url array
// use .map for cleaner code
urls.push(newURL);
}
// return a new promise with concurrency limit
return Promise.all(
urls.map(url => {
return limit(() => getDetails(newURL));
})
);
})
.catch(error => {
throw new Error(error);
});
}
getInventory(startURL)
.then(result => {
console.log(result);
})
.catch(error => {
console.err(error);
});
Resources:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://javascript.info/async-await
https://ponyfoo.com/articles/understanding-javascript-async-await

Try using callback instead of return
function getDetails(loadURL, callback){
nightmare.goto(loadURL)
.wait(2000)
.evaluate(function(callback){
var gigs = [];
$('.hidden-xs .used-vehicle').each(function(){
item = {}
item["year"] = $(this).attr('data-year')
item["make"] = $(this).attr('data-make')
item["model"] = $(this).attr('data-model')
item["body"] = $(this).attr('data-body')
item["color"] = $(this).attr('data-ext-color')
item["trim"] = $(this).attr('data-trim')
item["mileage"] = $(this).attr('data-mileage')
item["transmission"] = $(this).attr('data-transmission')
item["vin"] = $(this).find(".vehicle-overview").attr('id')
item["title"] = $(this).find(".vehicle-overview h2 a").text()
item["link"] = $(this).find(".vehicle-overview h2 a").attr('href')
item["price"] = $(this).find(".vehicle-content .price").text()
gigs.push(item)
})
callback(gigs)
})
.end()
.then(function(result){
var returnString = '';
for(gig in result){
returnString = returnString + result[gig].title + " " + result[gig].link + " " + result[gig].year + " " + result[gig].make + " " + result[gig].model + " " + result[gig].body + " " + result[gig].color + " " + result[gig].trim + " " + result[gig].transmission + " " + result[gig].vin + " " + result[gig].price + "\n"
}
callback(returnString)
})
}
// We will need to get the total amount of pages that we need to parse
function getInventory(sURL, callback){
nightmare.goto(sURL)
.wait(2000)
.evaluate(function(){
totals = [];
items = {}
totalCars = $('.total-found .count').text()
carsOnPage = $('.hidden-xs .used-vehicle').size()
items['carTotal'] = totalCars
items['onPage'] = carsOnPage
var pageCalc = (totalCars / carsOnPage)
items['tPages'] = Math.ceil(pageCalc)
totals.push(items)
return totals
})
.end()
.then(function(result){
var totalCars = '';
var totalPages = '';
for (item in result){
totalPages = result[item].tPages
totalCars = result[item].carTotal
}
counter = 0;
newURL = '';
returnDetails = '';
for (i =0; i < totalPages; i++){
if (i == 0){
newURL = sURL;
} else {
counter = i + 1;
newURL = sURL + "#action=im_ajax_call&perform=get_results&_post_id=5&page=" + counter + "&show_all_filters=false";
}
//console.log(newURL)
getINV = getDetails(newURL, function(returnString){
callback(returnString)
})
returnDetails = returnDetails + getINV
}
callback(returnDetails)
})
}
getInventory(startURL, function(result){
console.log(result)
})

Maybe the following will work, changed the loops to reduce and map, took out jQuery and made some small changes.
The most important ones are:
Getting an attribute from a html element returns a string, you should convert it to a number.
Got rid of the callbacks and have the functions return promises.
Here is the code:
const getDetails = loadURL =>
nightmare.goto(loadURL)//return promise here
.wait(2000)
.evaluate(
()=>
Array.from(document.querySelectorAll('.hidden-xs .used-vehicle'))
.reduce(
(all,item)=>
all.concat(
[
element.getAttribute('data-year'),
element.getAttribute('data-make'),
element.getAttribute('data-model'),
element.getAttribute('data-body'),
element.getAttribute('data-ext-color'),
element.getAttribute('data-trim'),
element.getAttribute('data-mileage'),
element.getAttribute('data-transmission'),
element.querySelector(".vehicle-overview").getAttribute('id'),
element.querySelector(".vehicle-overview h2 a").innerText,
element.querySelector(".vehicle-overview h2 a").getAttribute('href'),
element.querySelector(".vehicle-content .price").innerText
].join(" ")
),
[]//the all array
)
);
// We will need to get the total amount of pages that we need to parse
const getInventory = sURL =>
nightmare.goto(sURL)
.wait(2000)
.evaluate(
()=> {
//there is only one item here, not sure why you push it into totals
// and call it items
const item = {}
//getAttribute returns a string, parse it to number
totalCars = parseInt(document.querySelector('.total-found .count').innerText,10);
carsOnPage = document.querySelectorAll('.hidden-xs .used-vehicle').length;
item['carTotal'] = totalCars
item['onPage'] = carsOnPage
var pageCalc = (totalCars / carsOnPage)
item['tPages'] = Math.ceil(pageCalc)
return item;
}
)
.then(
totalItem =>{
var totalCars = '';
var totalPages = '';
totalPages = totalItem.tPages
totalCars = totalItem.carTotal
newURL = '';
returnDetails = '';
return Array.from(new Array(totalPages),(_,index)=>index+1)
.reduce(
(p,counter)=>
p.then(
results=>{
if (counter === 1) {
newURL = sURL;
} else {
newURL = sURL + "#action=im_ajax_call&perform=get_results&_post_id=5&page=" + counter + "&show_all_filters=false";
}
return getDetails(newURL)
.then(
result=>results.concat(result)
);
}
),
Promise.resolve([])
);
}
);
getInventory(startURL)
.then(
result=>
console.log(result)
).catch(
err=>
console.warn("Something went wrong:",err)
);

Learning promises and callbacks with asynchronous functions is quite an undertaking. I was able to get this to work with the following. Thank you all for your help and direction. Each answer sent me down a different rabbit hole to ultimately find the solution.
function getInventory(sURL){
nightmare.goto(sURL)
.wait(2000)
.evaluate(function(){
totals = [];
items = {}
totalCars = $('.total-found .count').text()
carsOnPage = $('.hidden-xs .used-vehicle').size()
items['carTotal'] = totalCars
items['onPage'] = carsOnPage
var pageCalc = (totalCars / carsOnPage)
items['tPages'] = Math.ceil(pageCalc)
totals.push(items)
return totals
})
.then(result => {
var totalCars = '';
var totalPages = '';
for (item in result){
totalPages = result[item].tPages
totalCars = result[item].carTotal
}
counter = 0;
let links = [];
let returnLinks = '';
newURL = '';
for (i = 0; i < totalPages; i++){
if (i == 0){
newURL = sURL;
} else {
counter = i + 1;
newURL = sURL + "#action=im_ajax_call&perform=get_results&_post_id=5&page=" + counter + "&show_all_filters=false";
}
links.push(newURL);
}
return links;
})
.then(results => {
var arrayLinks = results;
arrayLinks.reduce(function(accumulator, url){
return accumulator.then(function(newResults){
return nightmare.goto(url)
.wait(5000)
.evaluate(() => {
const gigs = [];
$(".hidden-xs .used-vehicle").each(function() {
item = {};
item["year"] = $(this).attr("data-year");
item["make"] = $(this).attr("data-make");
item["model"] = $(this).attr("data-model");
item["body"] = $(this).attr("data-body");
item["color"] = $(this).attr("data-ext-color");
item["trim"] = $(this).attr("data-trim");
item["mileage"] = $(this).attr("data-mileage");
item["transmission"] = $(this).attr("data-transmission");
item["vin"] = $(this).find(".vehicle-overview").attr("id");
item["title"] = $(this).find(".vehicle-overview h2 a").text();
item["link"] = $(this).find(".vehicle-overview h2 a").attr("href");
item["price"] = $(this).find(".vehicle-content .price").text();
gigs.push(item);
});
return gigs;
})
.then(detail => {
for (gig in detail) {
try {
var carVin = detail[gig].vin;
var carTitle = detail[gig].title;
var carDescrip = detail[gig].year + " " + detail[gig].make + " " + detail[gig].model;
var carURL = detail[gig].link;
var carMake = detail[gig].make;
var carModel = detail[gig].model;
var carYear = detail[gig].year;
var carMileageFull = detail[gig].mileage;
var carMileage = carMileageFull.replace(',', '');
var carTransmission = detail[gig].transmission;
var carBody = detail[gig].body;
var carPriceFull = detail[gig].price;
var carPriceFull = carPriceFull.replace('$', '');
var carPriceFull = carPriceFull.replace('*', '');
var carPriceFull = carPriceFull.replace(',', '');
var carPrice = carPriceFull.trim();
var dealerAddress = "{addr1: '"+ addressFull.addr1 +"', city: '"+ addressFull.city +"', region: '"+ addressFull.region +"', postal_code: '"+ addressFull.postal_code +"', country: '"+ addressFull.country +"'}";
var dealerLat = latLongFull.latitude;
var dealerLong = latLongFull.longitude;
var carColor = detail[gig].color;
arrSetup = [carVin, carTitle, carDescrip, carURL, carMake, carModel, carYear, carMileage, 'MI', '', '', 'AUTOMATIC', 'GASOLINE', 'OTHER', 'Other', carVin, 'OTHER', carPrice + " USD", dealerAddress, carColor, carPrice + " USD", 'AVAILABLE', 'USED', dealerLat, dealerLong];
newResults.push(arrSetup);
}
catch(error){
returnString += error;
}
}
return newResults;
})
.catch(error => {
throw new Error(error);
});
});
}, Promise.resolve([]))
.then(function(finalCall){
/*
We need to get the 3rd image on every vdp in the array. We will need to create a loop, go to the page, get the image and properly insert it into the proper array index
*/
finalCall.reduce(function(accumulator, resultArray){
return accumulator.then(function(finalResults){
var vdp = resultArray[3];
return nightmare.goto(vdp)
.wait(500)
.evaluate(() => {
var thirdIMG = $('.gallery-thumbs .owl-item:nth-of-type(3) img').attr('src');
return thirdIMG;
})
.then(imgResult => {
// 9
resultArray.splice(9, 1, imgResult);
console.log(resultArray);
finalResults.push(resultArray);
return finalResults;
})
.catch(error => {
throw new Error(error);
});
});
}, Promise.resolve([]))
.then(finalInsert => {
const csvWriter = createCsvWriter({
header: ["vehicle_id", "title", "description", "url", "make", "model", "year", "mileage.value", "mileage.unit", "image[0].url", "image[0].tag[0]", "transmission", "fuel_type", "body_style", "drivetrain", "vin", "condition", "price", "address", "exterior_color", "sale_price", "availability", "state_of_vehicle", "latitude", "longitude"],
path: 'test.csv'
});
var records = finalInsert;
console.log(records)
csvWriter.writeRecords(records)
.then(() => {
nightmare.end();
console.log('...Done');
});
})
});
})
.catch(function(error){
return error;
})
}
getInventory(startURL, function(response){
try {
console.log("This is the response" + response);
}
catch(error){
console.log(error)
}
});

Related

For loops breaks when using await

I have a problem when using await in a for loop. Every time it hits the await funtion it executes it fine, but it stops loping through the rest of the array that is looping through. I'm using nodejs with axios to send http request to a restAPI. The function that is in the api is big. So I thought maby that is the problem but it wasn't. Also the api uses its own await functions to, but that wasn't the problem either(As far as I know).
Here is my code for the request
var files = await globPromise("assets/series/**/*.json");
var json = null;
var file = null;
var series = [];
for (i = 0; i < files.length; i++) {
file = files[i];
var raw = fs.readFileSync(file);
json = JSON.parse(raw);
if (json.type === "series") {
try {
var userId = null;
if (req.user == null) {
userId = req.query.userid;
} else {
userId = req.user.id;
}
const response = await axios.get("http://" + host + "/series/info/" + json.id + "?userid=" + userId);
series.push(JSON.parse(response.data));
} catch (error) {
console.log(error);
series.push(json);
}
}
}
res.json(JSON.stringify(series));
});
And also the api side:
app.get('/series/info/:id', async(req, res) => {
var files = await globPromise("assets/series/**/*.json");
var json = null;
var file = null;
for (i = 0; i < files.length; i++) {
file = files[i];
var raw = fs.readFileSync(file);
json = JSON.parse(raw);
if (json.type === "series" && json.id === req.params.id) {
break;
}
json = null;
}
let path = pathFs.dirname(file) + "/";
try {
var seriesFiles = await fsPromise.readdir(path);
var latestWatchedVideo = null;
var latestWatchedTime = null;
var latestWatchTime = null;
var latestWatchDuration = null;
var seasonCount = 0;
var seasons = [];
for (i = 0; i < seriesFiles.length; i++) {
seriesFile = seriesFiles[i];
if (fs.lstatSync(path + "/" + seriesFile).isDirectory()) {
if (!seriesFile.startsWith("s")) {
continue;
}
seasonCount++;
try {
var videoFiles = await fsPromise.readdir(path + "/" + seriesFile + "/");
var videos = [];
for (let i = 0; i < videoFiles.length; i++) {
const video = videoFiles[i];
if (video.endsWith(".json")) {
var rawVideo = fs.readFileSync(path + "/" + seriesFile + "/" + video);
videoJson = JSON.parse(rawVideo);
const query = util.promisify(con.query).bind(con);
var userId = null;
if (req.user == null) {
userId = req.query.userid;
} else {
userId = req.user.id;
}
var results = await query(`SELECT * FROM watched WHERE video_id = "${videoJson.id}" AND user_id = "${userId}"`);
if (results.length > 0) {
var updated = JSON.parse(JSON.stringify(results[0].updated));
var duration = JSON.parse(JSON.stringify(results[0].duration));
var time = JSON.parse(JSON.stringify(results[0].time));
if (latestWatchedVideo == null) {
latestWatchedVideo = videoJson.id;
latestWatchedTime = updated;
latestWatchTime = time;
latestWatchDuration = duration;
} else {
if (latestWatchedTime < updated) {
latestWatchedVideo = videoJson.id;
latestWatchedTime = updated;
latestWatchTime = time;
latestWatchDuration = duration;
}
}
}
videos.push(videoJson);
}
}
function compare(a, b) {
if (a.episode < b.episode) {
return -1;
}
if (a.episode > b.episode) {
return 1;
}
return 0;
}
videos.sort(compare);
seasons.push({
season: seasonCount,
title: seriesFile.replace("s" + seasonCount, ""),
videos: videos
});
} catch (error) {
console.log(error);
}
}
}
json.seasonCount = seasonCount;
json.seasons = seasons;
json.latestWatchDuration = latestWatchDuration;
json.latestWatchTime = latestWatchTime;
json.latestWatchedVideo = latestWatchedVideo;
json.latestWatchedTime = latestWatchedTime;
res.json(JSON.stringify(json));
} catch (error) {
console.log(error);
}
});
Is there something (important) about await and async that I've missed?
Edit: my problem is that is loops fine through the first item and the await is working fine too, but it stops the loop and executes the next lines of code like there are no other items in my array.
Solved: I tried using the for/of and it works now. I don't know whats is so different between de default for and this one but it works!

Google function returning undefined

I have an issue with a custom google script I'm making to generate a bunch of sheets with info based on other sheets. I can't figure out why this is happening..
I've tried including logs and the values before the return is correct.. however when its returned, I get the value undefined.
it's regarding the function: getTournamentInfo(), called from tournamentInfo = getTournamentInfo(matchInfo[0]);
function getTournamentInfo(abbreviation) {
var sheet = ss.getSheetByName("Tournaments");
var tournaments = sheet.getRange("B2:B").getValues().filter(String);
console.log("Fetching Abbreviation: " + abbreviation);
var r = 2;
tournaments.forEach(function (tournament) {
if (tournament != "")
{
var tInfo = sheet.getRange("B"+r+":K"+r).getValues().toString().split(",");
if (tInfo[0] == abbreviation) {
console.log("Returning Info for: " + tInfo[0]);
return tInfo;
}
}
});
}
function generateSheets() {
var sheet = ss.getSheetByName("Match Schedule");
var matches = sheet.getRange("B5:B").getValues().filter(String);
var r = 5;
matches.forEach(function (match) {
if (match != "")
{
var matchInfo = sheet.getRange("B"+r+":L"+r).getValues().toString().split(",");
if (matchInfo[10] == "true") // Checks wether or not to generate the sheet
{
console.log("Generate = " + matchInfo[10]);
console.log("Fetching Tournament Info: " + matchInfo);
var tournamentInfo = "";
try {
tournamentInfo = getTournamentInfo(matchInfo[0]);
} catch (e) {
console.log(e);
}
console.log(tournamentInfo);
var template = "1v1PlayerTemplate"; // Default Template
if (tournamentInfo[3] == 2) {
template = "1v1TeamTemplate";
} else if (tournamentInfo[3] == 3) {
template = "XvXTeamTaplte";
}
var sheetName = matchInfo[0] + " | " + matchInfo[1];
var matchSheet = ss.getSheetByName(template).copyTo(ss.getSheetByName(template).getParent()).setName(sheetName);
}
}
r++;
});
}```
Your getTournamentInfo function is not returning your result. Your return statement only short-circuits the function supplied in forEach. This is one of the possible solutions (untested):
function getTournamentInfo(abbreviation) {
var sheet = ss.getSheetByName("Tournaments");
var tournaments = sheet.getRange("B2:B").getValues().filter(String);
console.log("Fetching Abbreviation: " + abbreviation);
var r = 2;
let result; // <----
tournaments.forEach(function (tournament) {
if (tournament != "" && result == undefined) { // <-----
var tInfo = sheet.getRange("B" + r + ":K" + r).getValues().toString().split(",");
if (tInfo[0] == abbreviation) {
console.log("Returning Info for: " + tInfo[0]);
result = tInfo; // <----
}
}
});
return result; // <----
}
You can do it more simply with a for loop, instead of forEach:
function getTournamentInfo(abbreviation) {
var sheet = ss.getSheetByName("Tournaments");
var tournaments = sheet.getRange("B2:B").getValues().filter(String);
console.log("Fetching Abbreviation: " + abbreviation);
var r = 2;
for (const tournament of tournaments) { // <==============
if (tournament != "") {
var tInfo = sheet.getRange("B" + r + ":K" + r).getValues().toString().split(",");
if (tInfo[0] == abbreviation) {
console.log("Returning Info for: " + tInfo[0]);
return tInfo;
}
}
}
}

find cities and country name from string

I have cities and countires list in a json file. For a given string, I need to check the city name and country name is present or not. If present I have to capitalize the word. what is the best way to acheive this in node JS
Please consider json from this link.
https://raw.githubusercontent.com/russ666/all-countries-and-cities-json/master/countries.json
my input is "united states to play davis cup in bratislava"
output should be "United States to play davis cup in Bratislava"
Hint: First letter of city and country name should be capital.
I am expecting code something like this
var myString="united states to play davis cup in bratislava";
var data=myjson;
var i=0;
myString=myString.split("");
for(i=0;i<myString.length;i++){
var output="";
//help this line
var check=myString[i].match(data)
if(check){
output+=myString[i].charAt(0).toUpperCase() + myString[i].slice(1);
}
else{
output+=myString[i]}
}
It all starts from function start() at the bottom. For displaying purpose i've used a small dataset but you can require the data from the json file by using const data = require('data.json'). I've tested for large dataset also, works like a charm. Hope it helps.
const data = {
"United States":[
"Washington","Bratislava","Hard","Going"],
"Afghanistan": [
"Herat",
"Kabul",
"Kandahar",
"Molah",
"Rana",
"Shar",
"Sharif",
"Wazir Akbar Khan"
]};
Array.prototype.myJoin = function(start,end){
if(!start) start = 0;
if(!end) end = this.length - 1;
end++;
return this.slice(start,end);
};
const getCityData = async (country) => {
return country;
}
const changeFormat = async () => {
try {
let countries = Object.keys(data).map( (country, index) => {
return country;
})
let citiesData = [];
await countries.map( (country, index) => {
citiesData = citiesData.concat(data[country]);
})
return countries.concat(citiesData);
} catch (err) {
return err;
}
}
const checkSentence = (text, text_original, number, modified_data) => {
return new Promise((resolve, reject)=>{
try {
if( !text || !text.length ){
throw new Error('empty text');
}
// console.log('started ' + number);
// console.log('number ' + number +' started')
let upperCase = [];
const number_const = number;
let temp1 = new Array(text.length);
temp1.fill(2);
temp1.map( (v, i) => {
// let temp = text;
let temp = [...text_original, ...[]];
// console.log('i' + i);
// console.log('number' + number);
if(i + number <= text.length ) {
// console.log('inside 1st if');
temp = temp.slice(i, i + number)
// console.log(text + ' 1');
temp = temp.join(' ')
// console.log(text + ' 2');
temp = temp.toLowerCase();
// console.log(text + ' 3');
if(modified_data.indexOf(temp) != -1){
upperCase.push({ start: i, end: i + number - 1 })
}
}
})
let toBeChanged = [];
if(upperCase.length){
upperCase.map( (v, i) => {
// console.log(v);
let arr = range( v.start, v.end )
toBeChanged = toBeChanged.concat(arr);
})
}
// console.log('ended number' + number);
// console.log(toBeChanged);
return resolve(toBeChanged);
} catch (err) {
return reject(err);
// console.error(err);
// return false;
}
})
}
const range = (start, end) => {
// console.log(start);
// console.log(end);
return Array(end - start + 1).fill().map((_, idx) => start + idx)
}
const start = async() => {
try {
excludeWords.map( (word, index) => {
excludeWords[index] = excludeWords[index].toLowerCase();
});
let modified_data_1 = await changeFormat();
let maximum = 1;
modified_data = modified_data_1.map( (v, i) => {
if(v.split(' ').length > maximum){
maximum = v.split(' ').length
}
if(excludeWords.indexOf(v.toLowerCase()) == -1) {
return v.toLowerCase();
}
});
text = text.split(' ');
if(maximum > text.length){
maximum = text.length;
}
// console.log(maximum);
let temp = new Array(maximum);
temp.fill(2);
let answer = await temp.map( (v, i) => {
let tempArray = [...text, ...[]];
let tempArray1 = [...text, ...[]];
return checkSentence(tempArray, tempArray1, (maximum - i), modified_data);
})
return Promise.all(answer).then( (results) => {
let merged = [].concat.apply([], results);
// console.log('merged');
merged = new Set(merged);
merged = [...merged];
// console.log(merged);
merged.map((v, i) => {
if(v == undefined || v == null){
return;
}
let temp1 = text[v].split('');
temp1[0] = temp1[0].toUpperCase();
text[v] = temp1.join('');
})
// console.log(text.join(' '));
return text.join(' ');
}).catch((err)=>{
console.log(err);
})
} catch (err) {
// console.error('here ERROR');
console.error(err);
return false;
}
}
let excludeWords = ['Hard', 'Going'];
let text = 'united states to davis cup hard wazir Akbar Khan in bratislava';
( async () => {
let answer = await start();
console.log(answer);
})();
Hi I have done in simple way it is also working. My problem is in the string the word "davis" also present as a city in json. How to not capitalize that word. For ex: "Hard", "Going" these words also have city name. but these words not be considered as city in my program.
Case 1:
Input: taiwan hit hard by sars outbreak.
Output should be: Taiwan hit hard by sars outbreak.
My output: Taiwan hit Hard by sars outbreak.
Please install capitalize npm and use data.json folder in your root folder to execute below code
var myData=require("./data");
var countriesArray=Object.keys(myData.data);
var citiesArray=Object.values(myData.data);
var capitalize=require('capitalize');
var citiesFlatten = [].concat.apply([], citiesArray);
var countryCities=countriesArray.concat(citiesFlatten);
var str = 'russia ponders space tourism deal';
var pattern = new RegExp("\\b("+countryCities.join("|")+")\\b","ig");
var matchArray=str.match(pattern);
if(!!matchArray){
matchArray.forEach(function(item) {
str=str.replace(item,capitalize.words(item));
});
console.log( str.replace(/^\w/, c => c.toUpperCase()));
}

Returning an array completed from an array.push inside a firebase foreach

i'm having bad time with nodejs (v6.14.0) at this moment, because i made a post request that have to do some work until return a valid response. The problem is that i get an empty array as response (if i rerun more than once the request the response will be valid), i have researched and need to manage how te promises are resolved and returned but i can't see what i need to do because i see this a bit more complex that the examples that i found, i need a more deeper advice or guide please. This is my code:
ru.post('/route', (request, response) => {
let rBody = request.body;
let weekDay = getDay(rBody.date); // Return day => "Monday"
let cRef = adminDb.database().ref('/fields/');
return cRef.orderByChild('field_size').equalTo(rBody.type).once('value').then((snap) => {
let arrResult = new Array;
snap.forEach(fields => {
infoField = fields.val();
let idFi = infoField.id_fi;
let idCen = infoField.id_cen;
let id_rsvp = idFi + "_" + rBody.date + "_" + idCen;
let id_rsvp2 = idFi + "_" + rBody.date + "_" + idCen + "_" + rBody.hour;
let resRef = adminDb.database().ref('/rsvp/' + id_rsvp + '/' + id_rsvp2);
resRef.on('value', snap => {
if (snap.val() === null) {
if (infoField.act === "true") {
let cenRef = adminDb.database().ref('/cen/' + idCen);
cenRef.on('value', snap => {
let infoCen = snap.val();
if (infoCen.act === "true") {
values = infoField.ft;
daySelected = weekDay;
dayCheck = values[daySelected];
hourSelected = rBody.hour;
hourCheck = dayCheck[hourSelected];
if (hourCheck !== "" && hourCheck !== "0") {
infoField.lat = infoCen.lat;
infoField.lon = infoCen.long;
if (rBody.lat !== undefined && rBody.long !== undefined) {
infoField.dGet = true;
} else {
infoField.dGet = false;
}
infoField.address = infoCen.address;
uRef = adminDb.database().ref('/users/');
uRef.child(rBody.userid).child('/fav/').orderByChild('id_fi').equalTo(idFi).on('value', snap => {
if (snap.exists() === true) {
infoField.fav = true
} else {
infoField.fav = false
}
arrResult.push(infoField); //how to get this arrResult (completed) to use on the next .then() on first try?
})
}
}
})
}
}
})
})
return arrResult;
}).then((res) => {
console.log("check",res);
return response.status(200).json(res); // Fist response is [], if it a new request with the same values it works...
}).catch(err => {
return response.status(err.status >= 100 && err.status < 600 ? err.code : 500).send(err.message);
})});
Resulting array must be something like this at fist try:
[{
//Values from first snapshot return (infoField) plus:
lat: infoCen.lat,
lon: infoCen.long,
dGet: true/false,
address: infoCen.address,
fav: true/false,
}]
I only get that after second running, at first one it keeps as empty []
I made some edits to your code, you are not handling promises correctly:
ru.post('/route', (request, response) => {
let rBody = request.body;
let weekDay = getDay(rBody.date); // Return day => "Monday"
let cRef = adminDb.database().ref('/fields/');
return cRef.orderByChild('field_size').equalTo(rBody.type).once('value').then((snapshot) => {
let arrResult = [];
snapshot.forEach(fields => {
let infoField = fields.val();
let idFi = infoField.id_fi;
let idCen = infoField.id_cen;
let id_rsvp = idFi + "_" + rBody.date + "_" + idCen;
let id_rsvp2 = idFi + "_" + rBody.date + "_" + idCen + "_" + rBody.hour;
let resRef = adminDb.database().ref('/rsvp/' + id_rsvp + '/' + id_rsvp2);
return resRef.on('value', snap => {
if (snap.val() === null) {
if (infoField.act === "true") {
let cenRef = adminDb.database().ref('/cen/' + idCen);
return cenRef.on('value', snap => {
let infoCen = snap.val();
if (infoCen.act === "true") {
let values = infoField.ft;
let daySelected = weekDay;
let dayCheck = values[daySelected];
let hourSelected = rBody.hour;
let hourCheck = dayCheck[hourSelected];
if (hourCheck !== "" && hourCheck !== "0") {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/lat').set(infoCen.lat));
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/lon').set(infoCen.long));
if (rBody.lat !== undefined && rBody.long !== undefined) {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/dGet').set(true));
} else {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/distanciaGet').set(false));
}
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/address').set(infoCen.address));
const uRef = adminDb.database().ref('/users/');
uRef.child(rBody.userid).child('/fav/').orderByChild('id_fi').equalTo(idFi).on('value', snap => {
if (snap.exists() === true) {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/fav').set(true));
} else {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/fav').set(false));
}
});
}
}
});
}
}
});
});
return Promise.all(arrResult);
}).then((res) => {
console.log("check", res);
return response.status(200).json(res); // Fist response is [], if it a new request with the same values it works...
}).catch(err => {
return response.status(err.status >= 100 && err.status < 600 ? err.code : 500).send(err.message);
});
});

Electron process in multithread doesn't close after error

Situation:
I want to create a multithread script where I load a list of IPs + account information with a CSV.
I load the data and call a function where I open electron and run my nightmare script in combination with Vo. Inside the script I go to a site, loop through a list of links and check if someone lives in Australia.
When I have an error, for example Timeout, the browser stops working.
Error Example ->
{ message: 'navigation error',
code: -7,
details: 'Navigation timed out after 30000 ms',
url: 'https://facebook.com/login' }
Here is my Code
var fs = require('fs');
var csv = require('fast-csv');
var vo = require('vo');
var Nightmare = require('nightmare');
var count = 0;
var urls = fs.readFileSync('uniqueIds.csv').toString().split("\n");
var arrayUrls = Object.keys(urls).map(function (key) {return urls[key]});
var bloqNumber = 0;
function *run(proxy, user, pass, urlsID) {
var nightmare = new Nightmare({
webPreferences: { partition: 'your-custom-partition'},
switches:{
'proxy-server': proxy,
'ignore-certificate-errors': true
}, show: true });
yield nightmare
.goto('https://facebook.com/login')
.wait(".inputtext._55r1.inputtext._1kbt.inputtext._1kbt")
.type('input[name="email"]', user)
.type('input[name="pass"]', pass)
.click('button[name=login]')
.wait(29000);
var range = urlsID * 2000;
var rangeStart = range - 2000;
var urlsarray = arrayUrls.slice(rangeStart, range);
for (var i = 0; i < urlsarray.length; i++) {
count++;
console.log(count + " -> " + proxy);
if (count > 150){
yield nightmare.end();
}
yield nightmare
.goto("https://www.facebook.com/profile.php?id=" + urlsarray[i] + "&sk=about&section=living&pnref=about")
.wait(1000);
var seqCheck = yield nightmare.exists(".captcha_interstitial");
var bloqCheck = yield nightmare.exists(".mvl.ptm.uiInterstitial.uiInterstitialLarge.uiBoxWhite");
if (seqCheck == true) {
console.log("Seqcheck");
yield nightmare.wait(29000);
}
if (bloqCheck == true) {
console.log("Blocked for a week" + user + proxy);
bloqNumber++;
console.log(bloqNumber);
if (bloqNumber > 6) {
yield nightmare.end();
}
continue;
}
var location = yield nightmare.exists("._3pw9._2pi4._2ge8");
bloqNumber = 0;
console.log(location);
if (location == true) {
var getLocation = yield nightmare.evaluate(function() {
var jsonObject = new Array();
var links = document.getElementsByClassName('_3pw9 _2pi4 _2ge8');
var numProfiles = links.length;
for(var i = 0; i< numProfiles; i++){
var elem;
try {
elem = links[0].querySelector("._50f5._50f7 a").text;
} catch (err) {
var arrr = new Array('Hello', 'world');
return arrr;
}
jsonObject.push(elem);
}
return jsonObject;
});
var locationString = getLocation.join(" + ");
console.log(locationString + " -> " + urlsarray[i]);
if (locationString.indexOf("Australia") !== -1 ||
locationString.indexOf("Queensland") !== -1 ||
locationString.indexOf("New South Wales") !== -1 ||
locationString.indexOf("Victoria") !== -1 ||
locationString.indexOf("Northern Territory") !== -1 ||
locationString.indexOf("South Australia") !== -1||
locationString.indexOf("Tasmania") !== -1 ||
locationString.indexOf("Sydney") !== -1 ||
locationString.indexOf("Adelaide") !== -1 ||
locationString.indexOf("Cairns") !== -1 ||
locationString.indexOf("Perth") !== -1 ||
locationString.indexOf("Melbourne") !== -1 ||
locationString.indexOf("Brisbane") !== -1 ||
locationString.indexOf("Bundaberg") !== -1 ||
locationString.indexOf("Canberra") !== -1 ||
locationString.indexOf("Newcastle") !== -1 ||
locationString.indexOf("Western Australia") !== -1 ) {
console.log("Im in australia");
var stringToPrint = urlsarray[i] + ", " + locationString + "\n";
fs.appendFile('pages.csv', stringToPrint.replace(/(\r\n|\n|\r)/gm,"") + "\n", function (err) {
console.log("a new entry");
});
}
} else {
console.log("It was false");
}
}
yield nightmare.end();
}
fs.createReadStream('proxies.csv')
.pipe(csv())
.on('data', function (data) {
var proxy = data[0];
var user = data[1];
var pass = data[2];
var urlsID = data[3];
console.log(urlsID);
console.log(user);
console.log(pass);
vo(run(proxy, user, pass, urlsID)).then(out => console.log('out', out)).catch(error => console.log(error));
}).on('end', function (data) {
console.log('CSV reading finished.')
});
Desired Outcome:
I want every time i get some kind of error that my thread is closing.
Solved. Just append .catch like in the example below.
yield nightmare
.goto('https://facebook.com/login')
.wait(".inputtext._55r1.inputtext._1kbt.inputtext._1kbt")
.type('input[name="email"]', user)
.type('input[name="pass"]', pass)
.click('button[name=login]')
.wait(29000).catch(function(err){
console.dir(err);
nightmare.end();
});

Categories