fs.readFileSync gives duplicated results when reading JSON files in node.js - node.js

I got two JSON files that I'm processing:
sorted/apr-total2.json
sorted/may-total2.json
There are files for other months of the year, but for debugging purposes I'm focusing on these two because they are causing the problem.
They contain simple data in the following format:
[
["John", 1403],
["Peter", 1159],
...
]
However, John is available only in sorted/apr-total2.json file and not in sorted/may-total2.json.
My code is the following:
let months = ["apr", "may"];
let dataToSort = {};
function getDataFromJSONFile(month) {
let jsonData = fs.readFileSync(`sorted/${month}-total2.json`);
let convertedData = JSON.parse(jsonData);
return convertedData;
}
function arrayToObject(data) {
data.forEach((user) => {
dataToSort[user[0].toString()] = user[1];
});
return dataToSort;
}
for (let i in months) {
let currentMonth = months[i];
console.log("Current month is " + currentMonth);
let getDataForMonth = getDataFromJSONFile(currentMonth);
let convertedMonth = arrayToObject(getDataForMonth);
if (convertedMonth.hasOwnProperty("John") == true) {
console.log(true);
}
}
My output is the following:
Current month is apr
true
Current month is may
true
This isn't correct, since user John isn't available in sorted/may-total2.json. So why is this happening? It seems that the object convertedMonth is causing the issue.
If I add the following code at the end of the for loop and delete the ConvertedMonth object properties:
for (let item in convertedMonth) {
delete convertedMonth[item];
}
It works as intended:
Current month is apr
true
Current month is may
I want to know why this object is causing the issue, because I reused the same code in another place of my project.

Your arrayToObject function uses a single global dataToSort, so any entries will be accumulated and overwritten there. See:
> let dataToSort = {};
> function arrayToObject(data) {
... data.forEach((user) => {
... dataToSort[user[0].toString()] = user[1];
... });
... return dataToSort;
... }
> arrayToObject([["foo", 123]])
{ foo: 123 }
> arrayToObject([["foo", 456], ["bar", 567]])
{ foo: 456, bar: 567 }
Your program simplifies to something like
let months = ["apr", "may"];
for (let currentMonth of months) {
console.log("Current month is " + currentMonth);
let jsonData = JSON.parse(fs.readFileSync(`sorted/${month}-total2.json`));
let convertedMonth = Object.fromEntries(jsonData);
if (convertedMonth["John"]) {
console.log(true);
}
}
which
uses for..of for correct and simpler iteration over the months
uses Object.fromEntries to convert an array of 2-arrays into a plain object (which is what your arrayToObject did)

Related

How to add Numbers In Arrays format if we have a word in 3 files using nodejs

I want to ask a question, what is it, I have three files, if there is a Word COMMON in those files then it should be printed like this [1,2 3], otherwise, if there is a word in 1 and 2 then it should be printed like this [1 2] , I Tried to PUSH ARRAY but it's not happening
Here Is My Code:
let Page1data = filtered.map((val) => {
let data = {};
if (Page1.includes(val)) {
data[val] = ["1"];
}
if (Page2.includes(val)) {
data[val] = ["2"];
}
if (Page3.includes(val)) {
data[val] = ["3"];
}
return data;
});
console.log(Page1data);
If I get it right, the problem is with your declaration.
.push() is for arrays not for objects. You have declared your data variable as an object.
You should use:
let data = [];
instead of
let data = {};
So it's going to look like this:
let data = [];
if (Page1.includes(val)) {
data.push("1");
}
etc...

Unable to run includes() method on string returned by fs.readFileSync()

having a bit of trouble:
let data = fs.readFileSync(pathToCsv, "utf8");
the value of data comes out to be:
clan,mem1,mem2,mem3,mem4,language,managerID,serverJoinedDate
pm,
pm
(through console.log())
but still data.toString().includes("pm") is false.
Here is my full code:
const filter = (m) => m.author.bot === false;
await ogMessage.author.dmChannel
.awaitMessages(filter, {
max: 1,
time: 60000,
})
.then((collected) => {
if (clans[parseInt(collected.toJSON()[0].content) - 1]) {
let clan = clans[parseInt(collected.toJSON()[0].content) - 1];
let data = fs.readFileSync(pathToCsv, "utf8");
console.log(typeof clan);
// let reg = new RegExp(clan, "g");
// let count = (data.match(reg) || []).length;
if (data.split(",").includes(clan)) {
ogMessage.author.send(
"People from this clan are already registered!\nPlease contact the hosts for help!"
);
return;
} else {
teamCheck = true;
}
} else {
ogMessage.author.send("Invalid Clan! Please try again!");
return;
}
})
.catch((collected) => {
try {
console.log("Error" + collected);
} catch (e) {
console.log(e);
}
});
if (teamCheck === false) {
return;
}
I have tried splitting the data, using regular expressions but nothing seems to work on the string returned by
readFileSync()
PS. I am making a discord bot.
In the source string you have pm with a space before it. That's why after calling split("," you end up with the element " pm" in the result array and "pm" is not equal to it.
You just need to trim spaces in all elements before searching some string in it
The problem was in the clans array.
I was defining it as such:
var clans = fs.readFileSync(pathToData, "utf8").split("\n");
but the problem was that the readFileSync() method added an "\r" after every string of the array. That is why it was not able to match the clan string to the data
So what worked was var clans = fs.readFileSync(pathToData, "utf8").split("\r\n");
Now, the array includes only the string, and the includes() method can find a match!

How do i get a randomly selected string name and still select an existing substring without the code coming out as text

I'm currently trying to get info off of an object but that's randomly selected. I believe that the true problem is that what I wrote is not being taken as a variable for selecting an existing object if not as the variable for the object, I don't know if this is a clear message or not.
Example of what I have tried:
let PK = ["Random1", "Random2", "Random3"]
let PKS = Math.floor(Math.random() * PK.length)
let Random1 = {
name: "Random1",
number: "010"
}
let Random2 = {
name: "Random2",
number: "011"
}
if(message.content.toLowerCase() == "random"){
message.channel.send(PK[PKS].number)
}
There is another thing I have tried which is by using ${}. These are the results:
"Random1.number" or "Random2.number" when what I was looking for is actually "010" or "011".
You should wrap your objects inside some collection such as an Array and then you just compare the value from your random selection to the value find in the collection (if any (randoms)):
let PK = ["Random1", "Random2", "Random3"];
let PKS = Math.floor(Math.random() * PK.length);
const randoms = [
{
name: "Random1",
number: "010",
},
{
name: "Random2",
number: "011",
},
];
if (message.content.toLowerCase() == "random") {
const findRandom = randoms.find((v) => v.name === PK[PKS]);
if (findRandom) {
message.channel.send(findRandom.number);
}
}

Gmail to Google Spread Sheet (only date, email and subject)

The code I have cobbled together does work, but it imports the wrong things from my email. I only want the date sent, the sender email address and the subject to import into the google sheet.
Can anyone help?
function onOpen() {
const spreadsheet = SpreadsheetApp.getActive();
let menuItems = [
{name: 'Gather emails', functionName: 'gather'},
];
spreadsheet.addMenu('SP LEGALS', menuItems);
}
function gather() {
let messages = getGmail();
let curSheet = SpreadsheetApp.getActive();
messages.forEach(message => {curSheet.appendRow(parseEmail(message))});
}
function getGmail() {
const query = "to:legals#salisburypost.com";
let threads = GmailApp.search(query,0,10);
let messages = [];
threads.forEach(thread => {
messages.push(thread.getMessages()[0].getPlainBody());
label.addToThread(thread);
});
return messages;
}
function parseEmail(message){
let parsed = message.replace(/,/g,'')
.replace(/\n*.+:/g,',')
.replace(/^,/,'')
.replace(/\n/g,'')
.split(',');
let result = [0,1,2,3,4,6].map(index => parsed[index]);
return result;
}
I believe your goal as follows.
You want to retrieve "the date, sender and subject" from the 1st message in the searched threads to the active sheet of Google Spreadsheet.
For this, how about this answer?
Modification points:
In this case, you can retrieve "the date, sender and subject" using the built-in methods for GmailApp.
When the values are put to the Spreadsheet, when appendRow is used in the loop, the process cost will become high.
It seems that label is not declared in your script.
When above points are reflected to your script, it becomes as follows.
Modified script:
In this modification, I modified gather() and getGmail() for achieving your goal.
function gather() {
let messages = getGmail();
if (messages.length > 0) {
let curSheet = SpreadsheetApp.getActiveSheet();
curSheet.getRange(curSheet.getLastRow() + 1, 1, messages.length, messages[0].length).setValues(messages);
}
}
function getGmail() {
const query = "to:legals#salisburypost.com";
let threads = GmailApp.search(query,0,10);
let messages = [];
threads.forEach(thread => {
const m = thread.getMessages()[0];
messages.push([m.getDate(), m.getFrom(), m.getSubject()]);
// label.addToThread(thread);
});
return messages;
}
When no threads are retrieved with "to:legals#salisburypost.com", the values are not put to the Spreadsheet. Please be careful this.
References:
getDate()
getFrom()
getSubject()
setValues(values)

moment.js reverse .fromNow()

So i'm working with moment.js.
I see you can translate a date into a human-friendly format using moment().fromNow();
Is there a way to do the opposite?
For example, I want to turn this --> "2 weeks ago" into a normal date format or UNIX timestamp.
I sifted through the documentation but couldnt find anything. Any direction would help, thanks.
Depending on how complicated/different the input strings can be, you could do this:
//parse out the number and the duration
var inputString = "2 weeks ago";
var myRegExp = /^(\d+)\s(\w+)\sago$/;
var results = myRegExp.exec(inputString);
var num = results[1];
var duration = results[2];
moment().subtract(duration,num).toString() //or whatever format you prefer
Note this will work for input strings of the format "number duration ago".
Hope that helps!
In some cases .fromNow() returns string like "30+ days ago". The regex provided in above solution doesn't handle to parse that properly.
Here's the updated regex to handle that case:
var myRegExp = /^(\d+)\+?\s(\w+)\sago$/;
Here is the method that I used to reverse it for the current moment.js locale. I tested it on a few locales and it should work for every locale but it might not.
Change the last two .toString() functions to .valueOf() to get numerical values.
Moment actually doesn't have the week name data for all languages right now, so the function will assume that the string is a week if it could not find the value.
Some languages use translation functions instead of having built in values so the script will not work on those either! If you manually specify your language data then it should work.
//test en locale
moment.locale("en");
console.log(reversefromNow("5 days ago"));
console.log(reversefromNow("in 5 days"));
//test ja locale
moment.locale("ja");
console.log(reversefromNow("5 日前"));
console.log(reversefromNow("5 日後"));
function reversefromNow(input) {
let relativeLocale = JSON.parse(JSON.stringify(moment.localeData()._relativeTime));
let pastfutureObject = {
future: relativeLocale.future,
past: relativeLocale.past
};
delete relativeLocale.future;
delete relativeLocale.past;
//detect if past or future
let pastfuture;
for (const [key, value] of Object.entries(pastfutureObject)) {
if (input.indexOf(value.replace("%s", "")) != -1) {
pastfuture = key;
}
}
//detect the time unit
let unitkey;
for (const [key, value] of Object.entries(relativeLocale)) {
if (input.indexOf(value.replace("%d", "")) != -1) {
unitkey = key.charAt(0);
}
}
//if its not in the data, then assume that it is a week
if (unitkey == null) {
unitkey = "w";
}
const units = {
M: "month",
d: "day",
h: "hour",
m: "minute",
s: "second",
w: "week",
y: "year"
};
//Detect number value
const regex = /(\d+)/g;
let numbervalue = input.match(regex) || [1];
//Add if future, subtract if past
if (pastfuture == "past") {
return moment().subtract(numbervalue, units[unitkey]).valueOf();
} else {
return moment().add(numbervalue, units[unitkey]).valueOf();
}
}

Resources