node.js edit variable from another file - javascript

Hello i have just started to learn node.js today and i am having a problem getting a variable from another file and editing it.
I'm trying to get the variable bots from server.js and edit it in bots.js.
server.js:
var bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(bots); }, 5000);
module.exports.bots = bots;
bots.js:
var request = require("../server.js");
var unique_id = '16f11103';
if (request.bots.some(e => e.botID === unique_id)) {
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
}

One way of doing this is to create a function that sets the variable bots.
Note: Instead of using seperate variables and functions i suggest to use something like a class to group all the functionality surrounding the bots.
server.js
var bots = [
{ botID: "16f11103", userID: "12345" },
{ botID: "5657d5e9", userID: "54321" },
];
var setBots = (newBots) => (bots = newBots);
setInterval(() => {
console.log(bots);
}, 5000);
module.exports = { bots, setBots };
bots.js
var { bots, setBots } = require("./server.js");
var unique_id = "16f11103";
if (bots.some((e) => e.botID === unique_id)) {
var newBots = bots.filter(function (e) {
return e.botID != unique_id;
});
setBots(newBots);
}

It's not working because of how variables are modified in JavaScript (Think copy by reference vs value).
When you first export the bots variable in server.js:
module.exports.bots = bots;
module.exports.bots is pointing to the bots variable by reference. If you modify the array directly like module.exports.bots[0] = 1 it will be reflected in the original bots variable as well since the reference hasn't changed.
However, what you're doing is reassigning the module.exports.bots value completely. The Array.prototype.filter() returns a new array without modifying the original.
When you do:
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
You're making it so that the module.exports.bots variable points to a new array and no longer points to the original bots array. That's why your console.log call is always printing the same array because the original bots variable is still pointing to that array and it was never modified.
If you don't want to modify the array directly you can try this.
server.js
var request = {};
request.bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(request.bots); }, 5000);
module.exports = request;
bots.js can now be kept exactly the same
var request = require("../server.js");
var unique_id = '16f11103';
if (request.bots.some(e => e.botID === unique_id)) {
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
}

You should do
var bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(bots); }, 5000);
module.exports = bots;
instead of
module.exports.bots = bots;
You can also do
exports.bots = bots;
Then use Like this
var bots = require("./server.js");
var unique_id = '16f11103';
if (bots.some(e => e.botID === unique_id)) {
bots = bots.filter(function(e) {
return e.botID != unique_id;
});
}

Related

Use a variable inside a function in other function

I have a scope problem, in this code when I call mp3.playlistInfo() ${titlePlayList} is undefined.
How can I use ${titlePlaylist} inside playlistInfo: function()
const mp3 = {
createPlaylist: function() {
let titlePlaylist = prompt("Choose a name: ")
console.log(`Your playlist name is: ${titlePlaylist}`)
},
playlistInfo: function() {
console.log(`Already listening ${titlePlaylist}`)
}
}
mp3.createPlaylist();
mp3.playlistInfo();
The issue is that you haven't declared titlePlaylist anywhere that's accessible for the other function.
You could solve this by just adding it to the mp3 object you created.
I would however suggest using a Class rather than an Object, if you were going to go with this approach.
Simple Object based approach
const mp3 = {
titlePlaylist: null,
createPlaylist: function() {
this.titlePlaylist = prompt("Choose a name: ")
console.log(`Your playlist name is: ${this.titlePlaylist}`)
},
playlistInfo: function() {
console.log(`Already listening ${this.titlePlaylist}`)
}
}
mp3.createPlaylist();
mp3.playlistInfo();
Simple Class based approach
class MP3 {
constructor() {
this.titlePlaylist = prompt("Choose a name: ");
this.playlistInfo();
}
playlistInfo() {
console.log(`Already listening ${this.titlePlaylist}`)
}
}
const mp3 = new MP3();
As mp3 is an object you'll need to set titlePlaylist as a property of mp3. Then in createPlaylist you need to set the returned value from the user to the value of titlePlaylist. You can then access titlePlaylist in playlistInfo, and if you use arrow functions you can skip using this.titlePlaylist:
const mp3 = {
titlePlaylist: "",
createPlaylist: () => {
titlePlaylist = prompt("Choose a name: ");
console.log(`Your playlist name is: ${titlePlaylist}`);
},
playlistInfo: () => {
console.log(`Already listening ${titlePlaylist}`);
},
};
mp3.createPlaylist();
mp3.playlistInfo();
You can also achieve this functionality by taking advantage of closures which allow you to get and set variables scoped within the original function body:
const mp3Closure = () => {
let titlePlaylist = "";
return {
createPlaylist: () => {
titlePlaylist = prompt("Choose a name: ");
console.log(`Your playlist name is: ${titlePlaylist}`);
},
playlistInfo: () => {
console.log(`Already listening ${titlePlaylist}`);
},
};
};
const mp3WithClosure = mp3Closure();
mp3WithClosure.createPlaylist();
mp3WithClosure.playlistInfo();
OOP variant if needed:
class MP3 {
constructor(titlePlaylist) {
this.titlePlaylist = titlePlaylist;
}
titlePlaylist = null;
get titlePlaylist() {
return this.titlePlaylist;
}
set titlePlaylist(value) {
this.titlePlaylist = value;
}
createPlaylist = () => {
this.titlePlaylist = prompt("Choose a name: ");
console.log(`Your playlist name is: ${this.titlePlaylist}`);
};
playlistInfo = () => {
console.log(`Already listening ${this.titlePlaylist}`);
};
}
const mp3 = new MP3("");
mp3.createPlaylist();
mp3.playlistInfo();

JavaScript - Targeting an object value to create another variable

So I have an array which looks like this:
[
{ TransactionValues: '50.00' },
{ TransactionValues: '-77.43' },
{ TransactionValues: '-20.23' },
{ TransactionValues: '200.23' }
]
I am trying to find a way to target the monetary value and create a variable based on the sum of these. When I try to target the "50.00" for example I get "Undefined" and it's still an array.
I'm not exactly sure how I can target it specifically, is it possible? Any help would be appreciated
As per the comments here is the full code (be wary I'm still learning so it's not elegant):
var fs = require('fs');
var parse = require('csv-parse');
var transactionValues = []; //Need an array to hold transactions
var currentTrans = [];
var savingsTrans = [];
//constuctor for transactions
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : transactions
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = {
"TransactionValues" : transactions
}
currentTrans.push(cTrans);
}
else {
var sTrans = {
"TransactionValues" : transactions
}
savingsTrans.push(sTrans);
}
};
//parses the csv file, loops each row and adds it to the transactionValue array
var parser = parse({columns: true}, function (err, results) {
console.table(results);
for (const row of results) {
addData(row.AccountID, row.AccountType, row.InitiatorType, row.DateTime, row.TransactionValue );
logTrans(row.AccountType, row.TransactionValue);
}
console.log(transactionValues);
console.log(currentTrans);
console.log(savingsTrans);
});
fs.createReadStream(__dirname+'/testData/customer-1234567-ledger.csv').pipe(parser)
not completely following but at the end of the day you have an array like data below.
you can use filter to target the attribute you want.
you can use map to pull out just the values.
you can use reduce to sum them all up.
run the snippet below to see each step
const data = [
{ TransactionValues: '50.00', AccountType: 'CURRENT' },
{ TransactionValues: '-77.43', AccountType: null},
{ TransactionValues: '-20.23', AccountType: 'CURRENT' },
{ TransactionValues: '200.23', AccountType: null }
];
const CurrentTrans = data.filter((x) => x.AccountType === 'CURRENT');
const SavingTrans = data.filter((x) => x.AccountType !== 'CURRENT');
console.log('CurrentTrans');
console.log(CurrentTrans);
console.log('SavingTrans');
console.log(SavingTrans);
const CurrentTransValues = CurrentTrans.map((x) => parseFloat(x.TransactionValues));
const SavingTransValues = SavingTrans.map((x) => parseFloat(x.TransactionValues));
console.log('CurrentTransValues');
console.log(CurrentTransValues);
console.log('SavingTransValues');
console.log(SavingTransValues);
const TotalCurrentValues = CurrentTransValues.reduce((sum, x) => sum + x);
const TotalSavingValues = SavingTransValues.reduce((sum, x) => sum + x);
console.log('TotalCurrentValues');
console.log(TotalCurrentValues.toFixed(2));
console.log('TotalSavingValues');
console.log(TotalSavingValues.toFixed(2));
So I may have fixed it by using parseFloat in my addData and logTrans functions:
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : parseFloat(transactions)
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = parseFloat(transactions);
currentTrans.push(cTrans);
}
else {
var sTrans = parseFloat(transactions);
savingsTrans.push(sTrans);
}
};
Now that seems to of worked. So I can use the "Sum values of objects in array" as suggested before. Thank you everyone :)

Matching a string to one of many patterns and extracting data

I have a problem I want to solve with RegEx, or any other method if there is a better one. I've tried several ways to achieve the goal, but nothing really worked.
I have an array with endpoints:
const endpoints = [
{
id: 1,
url: "/api/items/:itemId"
},
{
id: 2,
url: "/api/users/:userName/delete"
},
{
id: 3,
url: "/api/users/:userName/edit"
}
];
And a request URL:
const url = "/api/users/max/edit";
Now what I want is to have a function which acts like this:
const rewrite = (url, endpoints) => {
// What is the best way to achieve the following return value:
return {
endpointId: 3,
values: {
userName: "max"
}
};
};
Explanation: The function should find the appropriate endpoint for the url. All parts of the endpoint url which start with a colon are not static, but should rather be replaced with values from the request url. In this case :userName should be replaced with max.
I've been in web development for some time now, but to be honest I've almost no clue how to solve such a problem.
const rewrite = (url, endpoints) => {
var doubledArray = Array.prototype.map.call(endpoints, function(el) {
return {
id: el.id,
url: el.url.split('/')
};
});
var parts = url.split('/');
var i = 0;
parts.forEach(function(element) {
doubledArray = doubledArray.filter(el => (element == el.url[i] || el.url[i].startsWith(':')));
i++;
});
return {
endpointId: doubledArray[0].id,
values: {
[`${doubledArray[0].url.filter(el => el.startsWith(':'))[0].substring(1)}`]: parts[doubledArray[0].url.findIndex(function (el) { return el.startsWith(':'); } )],
}
};
};
You can go through the endpoints making each .url into a RegExp to test the url against.
When a matching one is found, it is just a matter of extracting the needed part and making up an Object with the property name:
<script>
const myEndpoints = [
{
id: 1,
url: "/api/items/:itemId"
},
{
id: 2,
url: "/api/users/:userName/delete"
},
{
id: 3,
url: "/api/users/:userName/edit"
}
];
const myUrl = "/api/users/nermal/edit";
const rewrite = (url, endpoints) => {
for (let i = 0; i < endpoints.length; i++) {
var rep = new RegExp(":(\\w+)", "m");
var propName = rep.exec(endpoints[i].url);
var reu = new RegExp(endpoints[i].url.replace(propName[0], "(.*)"));
var a = reu.exec(url);
if (a !== null) {
var x = new Object;
x["endpointId"] = endpoints[i].id;
var y = new Object;
y[propName[1]] = a[1];
x["values"] = y;
return x;
}
}
return null;
};
var q = rewrite(myUrl, myEndpoints);
console.log(q);
console.log(q.values);
</script>
Outputs:
Object { endpointId: 3, values: {…} }
Object { userName: "nermal" }

Object properties are undefined, the object itself shows all the data though

I'm building an Single Page application for a minor project. I've been trying to save data from API call's to the Movie Database in an object. If i console.log the object, I can see all its properties and values. If I console.log the object.property, it returns 'undefined'. This is the code:
(() => {
"use strict"
/* Saving sections to variables
--------------------------------------------------------------*/
const movieList = document.getElementsByClassName('movie_list')[0];
const movieSingle = document.getElementsByClassName('movie_single')[0];
/* All standard filters for displaying movies
--------------------------------------------------------------*/
const allFilters = {
trending: 'movie/popular',
toplist: 'movie/top_rated',
latest: 'movie/now_playing',
upcoming: 'movie/upcoming'
};
const allData = {};
/* Initialize app - Get al standard data and save it in object
--------------------------------------------------------------*/
const app = {
init() {
getData(allFilters.trending, 'popular');
getData(allFilters.toplist, 'toplist');
getData(allFilters.latest, 'latest');
getData(allFilters.upcoming, 'upcoming');
this.startPage();
},
startPage() {
window.location.hash = "trending";
}
}
/* Function for getting data from the API
--------------------------------------------------------------*/
const getData = (filter, key) => {
const request = new XMLHttpRequest();
const apiKey = '?api_key=xxx';
const getUrl = `https://api.themoviedb.org/3/${filter}${apiKey}`;
request.open('GET', getUrl, true);
request.onload = () => {
if (request.status >= 200 && request.status < 400) {
let data = JSON.parse(request.responseText);
data.filter = key;
cleanData.init(data);
} else {
window.location.hash = 'random';
}
};
request.onerror = () => {
console.error('Error');
};
request.send();
};
/* Check if the data is list or single, and clean up
--------------------------------------------------------------*/
const cleanData = {
init(originalData) {
if (!originalData.results) {
this.single(originalData);
} else {
allData[originalData.filter] = originalData;
}
},
list(data) {
data.results.map(function(el) {
el.backdrop_path = `https://image.tmdb.org/t/p/w500/${el.backdrop_path}`;
});
let attributes = {
movie_image: {
src: function() {
return this.backdrop_path;
},
alt: function() {
return this.title;
}
},
title_url: {
href: function() {
return `#movie/${this.id}/${this.title}`;
}
}
}
showList(data.results, attributes);
},
single(data) {
data.poster_path = `https://image.tmdb.org/t/p/w500/${data.poster_path}`;
data.budget = formatCurrency(data.budget);
data.revenue = formatCurrency(data.revenue);
data.runtime = `${(data.runtime / 60).toFixed(1)} uur`;
data.imdb_id = `http://www.imdb.com/title/${data.imdb_id}`;
let attributes = {
movie_image: {
src: function() {
return this.poster_path;
},
alt: function() {
return this.title;
}
},
imdb_url: {
href: function() {
return this.imdb_id
}
},
similar_url: {
href: function() {
return `#movie/${this.id}/${this.title}/similar`
}
}
};
showSingle(data, attributes);
}
};
const showList = (cleanedData, attributes) => {
movieList.classList.remove('hidden');
movieSingle.classList.add('hidden');
Transparency.render(movieList, cleanedData, attributes);
};
const showSingle = (cleanedData, attributes) => {
movieSingle.classList.remove('hidden');
movieList.classList.add('hidden');
Transparency.render(movieSingle, cleanedData, attributes);
}
const formatCurrency = amount => {
amount = amount.toFixed(0).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? '.' + c : c;
});
return `€${amount},-`;
};
app.init();
console.log(allData); // Returns object with 4 properties: trending, toplist, latest & upcoming. Each property is filled with 20 results (movies with data) from the API.
console.log(allData.trending) // Returns 'undefined' (each property I've tried).
console.log(allData['trending']) // Returns 'undefined'
Object.keys(allData); // Returns an empty Array []
})();
When I use console.log(allData) I see 4 properties, all filled with the results from the API. But when i do console.log(allData.trending) or console.log(allData['trending']) it returns 'Undefined' in the console. Has anyone an idea how to fix this?
When you call app.init() it fires the init and sends the api call(s) to fetch data.
The call to fetch data is Asynchronous, which means it doesn't wait for the response to continue execution. So it goes ahead and executes next lines of code, which are your console.logs. At this time the API calls haven't responded with the data, so when you try to access data.property it fails as the data is not yet here.
When you do log(data) it creates a log of the reference to data, which gets updated in the log when the reference is filled with a value later. To get the value of data at that instance and prevent updating later you can try log(JSON.stringify(data)). When you do that you get a consistent result, none of your logs work, which is the actual behaviour.
To get your logs work, look into the success/load callback of you A(synchronous)JAX request, log it from there. Or if you constructed allData later in cleanData then call the logs after cleanData function.
So to answer your question, none of your logs should work as it is an asynchronous call. You are getting the allData logged due to the way console.log works with references that are updated later, use console.log(JSON.stringify(allData)) to get the actual snapshot of Object

KnockOutJS trigger parent function on child subscribe

I am currently trying to learn KnockOutJS. I thought it would be a great idea to create a simple task-list application.
I do not want to write a long text here, let's dive into my problem. I appreciate all kind of help - I am new to KnockOutJS tho!
The tasks are declared as followed:
var Task = function (data) {
var self = this;
self.name = ko.observable(data.name);
self.status = ko.observable(data.status);
self.priority = ko.observable(data.priority);
}
And the view model looks like this
var TaskListViewModel = function() {
var self = this;
self.currentTask = ko.observable();
self.currentTask(new Task({ name: "", status: false, priority: new Priority({ name: "", value: 0 }) }));
self.tasksArr = ko.observableArray();
self.tasks = ko.computed(function () {
return self.tasksArr.slice().sort(self.sortTasks);
}, self);
self.sortTasks = function (l, r) {
if (l.status() != r.status()) {
if (l.status()) return 1;
else return -1;
}
return (l.priority().value > r.priority().value) ? 1 : -1;
};
self.priorities = [
new Priority({ name: "Low", value: 3 }),
new Priority({ name: "Medium", value: 2 }),
new Priority({ name: "High", value: 1 })
];
// Adds a task to the list
// also saves updated task list to localstorage
self.addTask = function () {
self.tasksArr.push(new Task({ name: self.currentTask().name(), status: false, priority: self.currentTask().priority() }));
self.localStorageSave();
self.currentTask().name("");
};
// Removes a task to a list
// also saves updated task list to localstorage
self.removeTask = function (task) {
self.tasksArr.remove(task);
self.localStorageSave();
};
// Simple test function to check if event is fired.
self.testFunction = function (task) {
console.log("Test function called");
};
// Saves all tasks to localStorage
self.localStorageSave = function () {
localStorage.setItem("romaTasks", ko.toJSON(self.tasksArr));
};
// loads saved data from localstorage and parses them correctly.
self.localStorageLoad = function () {
var parsed = JSON.parse(localStorage.getItem("romaTasks"));
if (parsed != null) {
var tTask = null;
for (var i = 0; i < parsed.length; i++) {
tTask = new Task({
name: parsed[i].name,
status: parsed[i].status,
priority: new Priority({
name: parsed[i].priority.name,
value: parsed[i].priority.value
})
});
self.tasksArr.push(tTask);
}
}
};
self.localStorageLoad();
}
What I want to do in my html is pretty simple.
All tasks I have added are saved to localStorage. The save function is, as you can see, called each time an element has been added & removed. But I also want to save as soon as the status of each task has been changed, but it is not possible to use subscribe here, such as
self.status.subscribe(function() {});
because I cannot access self.tasksArr from the Task class.
Any idea? Is it possible to make the self.tasksArr public somehow?
Thanks in advance!
Try this:
self.addTask = function () {
var myTask = new Task({ name: self.currentTask().name(), status: false, priority: self.currentTask().priority() })
myTask.status.subscribe(function (newValue) {
self.localStorageSave();
});
self.tasksArr.push(myTask);
self.localStorageSave();
self.currentTask().name("");
};

Categories

Resources