Passing function in other functions as parameters - javascript

I've recently started studying programming and JS, HTML, CSS.
Reading a book at the moment which includes the following snippet of code which i try to understand and modify for my own practice and attempts in understanding. But i really can't grasp what's going on.
Is there anyone that could please try and explain what's happening and why my modified snippet of code won't run - as it does look similar to part of the original one that does run fine.
First snippet attached is original one from book.
Second is mine which is built on parts of the prior one.
var validateDataForAge = function(data) {
person = data();
console.log(person);
if (person.age <1 || person.age > 99){
return true;
} else{
return false;
}
};
var errorHandlerForAge = function(error) {
console.log("Error while processing age");
};
function parseRequest(data,validateData,errorHandler) {
var error = validateData(data);
if (!error) {
console.log("no errors");
} else {
errorHandler();
}
}
var generateDataForScientist = function() {
return {
name: "Albert Einstein",
age : Math.floor(Math.random() * (100 - 1)) + 1,
};
};
//parse request
parseRequest(generateDataForScientist, validateDataForAge,
errorHandlerForAge);
var validateAge = function(age) {
person = age();
console.log(age);
}
validateAge(17);
I get following errormessage:
TypeError: age is not a function
at validateAge:2:12
at eval:7:1
at eval
at new Promise
Thankful for any help.
Regards,

Here is the code you are looking at. It expects data to be a function.
var validateDataForAge = function(data) {
person = data();
… and so it is (the one assigned to generateDataForScientist after it gets passed through a couple of other variables and function calls).
Here is your code:
var validateAge = function(age) {
person = age();
It expects age to be a function.
Here you pass it a value:
validateAge(17);
17 is a number, not a function.

The problem is here in this part of your code
var generateDataForScientist = function() {
return {
name: "Albert Einstein",
age : Math.floor(Math.random() * (100 - 1)) + 1, // <- problem
};
};
age is just a property of the object your returning and you are using age as a function in this code
var validateAge = function(age) {
person = age(); // <-- here ,age is not a function
console.log(age);
}
you need to chage the age property to be a function like this
var generateDataForScientist = function() {
return {
name: "Albert Einstein",
age : function (){ return Math.floor(Math.random() * (100 - 1)) + 1 },
};
};

consoling data inside parseRequest will log it as a function. But you need the property age. So replace validateData(data) with validateData(data());
var validateDataForAge = function(data) {
let person = data;
if (person.age < 1 || person.age > 99) {
return true;
} else {
return false;
}
};
var errorHandlerForAge = function(error) {
console.log("Error while processing age");
};
function parseRequest(data, validateData, errorHandler) {
console.log(data)
var error = validateData(data());
if (!error) {
console.log(error);
} else {
errorHandler();
}
}
var generateDataForScientist = function() {
return {
name: "Albert Einstein",
age: Math.floor(Math.random() * (100 - 1)) + 1,
};
};
//parse request
parseRequest(generateDataForScientist, validateDataForAge, errorHandlerForAge);

Related

Variable substitution within a string

I'm trying to come up with some very reusable code that will look up and perform variable substitutions within a string.
The example string below contains a $$ reference to a variable. Format is varname.key.
I want the subText() function to be reusable. The issue I'm having is repvars themselves can require substitution. The code hasn't finished substituting the example text and I'm asking it to substitute the repvars.cr by calling the same function. This seems to through it off. I'm saying that because if I do it separately in works.
var exampleText = "A string of unlimited length with various variable substitutions included $$repvars.cr$$";
var repvars = {
cr: 'Copyright for this is $$repvars.year$$',
year: '2019'
}
function subText(text) {
var subVars = findSubs(text);
return makeSubs(text, subVars);
}
function findSubs(theText) {
var subarr = [];
while (theText.indexOf('$$') > -1) {
theText = theText.substring(theText.indexOf('$$') + 2);
subarr.push(theText.substring(0, theText.indexOf('$$')));
theText = theText.substring(theText.indexOf('$$') + 2);
}
return subarr;
}
function makeSubs(text, subs) {
for (var s = 0; s < subs.length; s++) {
var subst = getSubVal(subs[s]);
text = text.split("$$" + subs[s] + "$$").join(subst);
}
return text;
}
function getSubVal(subvar) {
var subspl = subvar.split('.');
switch (subspl[0]) {
default:
return processRepVar(subspl[1]);
}
}
function processRepVar(rvName) {
var data = getRepVarData(rvName);
if(data.indexOf('$$') > -1) {
subText(data);
} else {
return data;
}
}
function getRepVars() {
return repvars;
}
function getRepVarData(key) {
return getRepVars()[key];
}
subText(exampleText);
Aren't you just missing a return here?
function processRepVar(rvName) {
var data = getRepVarData(rvName);
if(data.indexOf('$$') > -1) {
subText(data);
} else {
return data;
}
}
Changing subText(data) to return subText(data); makes your code work for me.
Working jsfiddle: https://jsfiddle.net/uzxno754/
Have you tried regular expressions for this?
function replace(str, data) {
let re = /\$\$(\w+)\$\$/g;
while (re.test(str))
str = str.replace(re, (_, w) => data[w]);
return str;
}
//
var exampleText = "A string with variables $$cr$$";
var repvars = {
cr: 'Copyright for this is $$year$$',
year: '2019'
}
console.log(replace(exampleText, repvars))
Basically, this repeatedly replaces $$...$$ things in a string until there are no more.

(Very) Novice Javascripter Needing Help Understanding Practical Functions

So I am building an election script that assigns candidates to partys and electorates then assigns them their votes but I am so scrambled so if someone can help me understand what I'm doing, for instance this function here:
class Electorate {
constructor (newNames) {
this.name = newNames
this.allMyElectorates = []
this.addWinner = []
this.addPartyVotes = []
}
addElectorate (...newNames) {
for(let i=0;i<newNames.length;i++){
var newElectorate = new Electorate(newNames[i], this.addWinner,
this.addPartyVotes)
this.allMyElectorates.push(newElectorate)
return newElectorate
}
}
addWinner(...newNames){
theParty = myElection.findParty (partyName)
if (theParty == undefined){
myElection.addIndependant (cadidateName)
}else{
theCandidate = theParty.findCandidate (candidateName)
}if (theCandidate == undefined) {
theParty.addElectorateMP (candidateName, this)
}else{
theCandidate.setElectorate (this)}
}
function totalVotes(...newNumbers){
for(let i=0;i<newNumbers.length;i++){
var newTotalVotes = new totalVotes(newNumbers[i], this)
this.addPartyVotes.push(newTotalVotes)
return newTotalVotes
}
}
function addWinner(...newNames){
for(let i=0;i<newNumbers.length;i++){
var addWinner = new Winner(newNames[i], this)
this.addWinner.push(newWinner)
return addWinner
}
}
}
And this is what I'm trying to reference at the moment:
anElectorate = theElection.addElectorate('Auckland Central')
anElectorate.addWinner('KAYE, Nicola Laura', 'National Party')
anElectorate.addPartyVotes(329, 85, 10, 486, 3, 2, 6242, 553, 6101, 158,
12652, 1459, 7, 17, 53, 99)
I want to create a new function (totalVotes) using data collected from addPartyVotes (in the controller class) that has to to be called from other classes, it has it's variables and I'm pushing it out in an array then returning it so what am I doing wrong?
I've tried asking people in my class and the tutors but I feel like they just fob me off without giving me any real guidance, I'm an engineer not a programmer so this is very difficult to wrap my head around.
There is no single line or point of code which is breaking your program.
There are countless errors, and will simply not function.
Take a look at this example (JS ES5):
var Electorate = {
candidates: [],
init: function() {
this.candidates = [];
return this;
},
getCandidateByName: function(name) {
var filter_candidate_by_name = this.candidates.filter(function(d) {
return d.name === name;
})[0];
var index_of_candidate = this.candidates.indexOf(filter_candidate_by_name);
return this.candidates[index_of_candidate];
},
calculateWinner: function() {
var max_votes = Math.max.apply(Math, this.candidates.map(function(d) {
return d.votes.length;
}));
if (!max_votes || isNaN(max_votes)) return false;
var records_with_max_votes = this.candidates.filter(function(d) {
return d.votes.length === max_votes;
});
var result = {};
if (records_with_max_votes.length > 1) {
result.result = 'Tied';
result.candidates = records_with_max_votes;
var list_of_candidate_names = records_with_max_votes.map(function(d) {
return d.name;
}).join(', ');
result.explaination = 'Tied between ' + list_of_candidate_names + ', with a count of ' + max_votes + ' votes each';
} else if (records_with_max_votes.length === 1) {
result.result = 'Won';
result.candidate = records_with_max_votes[0];
result.explaination = records_with_max_votes[0].name + ' won with a count of ' + max_votes + ' votes';
}
return result;
}
};
var Voter = {
name: null,
age: null,
gender: null,
init: function(name, age, gender) {
if (!name || !age || !gender) return false;
this.name = name;
this.age = age;
this.gender = gender;
return this;
}
};
var Candidate = {
name: null,
votes: [],
init: function(name) {
this.name = name;
this.votes = [];
return this;
},
castVote: function(voter) {
this.votes.push(voter);
return this;
}
};
var electorate = Object.create(Electorate).init();
electorate.candidates.push(
Object.create(Candidate).init('Mr. John Smith'),
Object.create(Candidate).init('Mr. Adam John'),
Object.create(Candidate).init('Ms. Christina Brown')
);
electorate
.getCandidateByName('Mr. John Smith')
.castVote(Object.create(Voter).init('Maria Smith', 38, 'Female'))
.castVote(Object.create(Voter).init('John Doe', 118, 'Male'))
.castVote(Object.create(Voter).init('John Doe', 44, 'Male'));
electorate
.getCandidateByName('Ms. Christina Brown')
.castVote(Object.create(Voter).init('John Doe', 235, 'Male'))
.castVote(Object.create(Voter).init('John Doe', 34, 'Male'))
.castVote(Object.create(Voter).init('John Doe', 22, 'Male'));
console.log(electorate.calculateWinner());
You can see that it collects information and takes into account Candidates and Voters which can be created and added to the appropriate data location in the Electorate.
It can then go ahead after all votes are cast, and announce the selected winner or the tied winners.
My advice is to brush up on your Javascript knowledge, and try not using ES6 additions.
This is a great resource to brush up on Javascript (for all levels of experience): https://github.com/getify/You-Dont-Know-JS
Functions cannot take this as an argument.
A valid function would look like this:
function totalVotes ( vote ) {
return "something";
}
If you can share your entire script pertaining to the voting/election program, I could help guide you and your approach to writing effective code.

NodeJS - Events.js cannot read property forEach of undefined

I'm facing an issue since 2 days and i can't figure out how to fix it. I've an error on forEach, so my application runs well and then stops without any explication.
Here is the code where the error happens.
var easy = setInterval(function(){
keywords.forEach(function(k) {
tweetModel.find({keyword: k}).sort({date: -1}).limit(20).exec(function(err, data) {
var score = [];
var date = [];
console.log(data);
console.log(err)
data.forEach(function (item) {
score.push(Math.floor(parseFloat(item.score) * 1000) / 1000);
date.push(item.date.getDate()+'/'+parseInt(item.date.getMonth() + 1)+'/'+item.date.getFullYear()+':'+parseInt(item.date.getHours() + 1)+':'+item.date.getMinutes());
tArrayStats[k] = score;
tArrayStats['date'] = date;
});
});
});
io.sockets.emit('stats',tArrayStats);
},3000);
The error is thrown here
data.forEach(function (item)
but i can't figure out why ! Thanks for you help.
As asked there is the output of console log data :
EDITED working code, thanks to #Ids van der Zee
var easy = setInterval(function(){
keywords.forEach(function(k) {
tweetModel.find({keyword: k}).sort({date: -1}).limit(20).exec(function(err, data) {
if (data && !err)
{
var score = [];
var date = [];
console.log(data);
console.log(err)
data.forEach(function (item) {
score.push(Math.floor(parseFloat(item.score) * 1000) / 1000);
date.push(item.date.getDate()+'/'+parseInt(item.date.getMonth() + 1)+'/'+item.date.getFullYear()+':'+parseInt(item.date.getHours() + 1)+':'+item.date.getMinutes());
tArrayStats[k] = score;
tArrayStats['date'] = date;
});
}
});
});
io.sockets.emit('stats',tArrayStats);
},3000);
on the line:
tweetModel.find({keyword: k}).sort({date: -1}).limit(20).exec(function(err, data) {
you are trying to find data corresponding to the keyword k, you are doing this for each keyword. If tweetModel does not contain the keyword you are looking for the data variable will be undefined. You can resolve this by checking if data is not undefined. Instead of
data.forEach(function (item){...
if(data){
data.forEach(function (item){...

pass by reference. Not working

Passing object to function is not working.
function getSecret(file, secretPassword){
file.opened = file.opened + 1;
if(secretPassword == file.password){
return file.contents;
}
else {
return "Invalid password! No secret for you";
}
}
function setSecret(file, secretPassword, secret){
if(secretPassword == file.password){
file.opened = 0;
file.contents = secret;
}
}
var superSecretFile = {
level: "classified",
opened: 0,
password: 2,
contents: "Dr. Evel's next meeting is in Detroit."
};
var secret = getSecret(superSecretFile, superSecretFile.password);** why superSecretFile.password is not passing the value 2
console.log(secret);
setSecret(superSecretFile,2, "Dr. Evel's next meeting is in Philadelphia.");
secret = getSecret(superSecretFile, superSecretFile.password);
console.log(secret);
I am passing an object as an argument. This code doesn't work if I put superSecretFile.password. Why it doesn't?

Setting an empty array index

i have an array that holds student answers for given questions.
if a student gives an answer, it gets inserted into the array at the current index like answers[questionindex] = answer
later, i can read the array and map the entries to the given question-array
this case:
[
"answer",
undefined, // student has not given answer
"answer2",
]
works. (looping over the array, simply outputting "no answer given" if (answers[questionindex] === undefined)
but it doesn't work when the LAST answers were undefined (1 or more)
they just don't exist (of course).
how can i set those fields to undefined (like, after a timer reaches zero), to show that there was no answer given?
right now, the average-calculation shows 100% correct for 3 given (correctly), then 2 not given at all
code
var testResults = {
addRoom: function(Id, teacher) { // room pseudoconstructor
this[Id] = {
created: moment(),
runningProblem: false,
time: 0, // holds the countdown for the current problem
getTime: function() { // returns the countdown-counter
return this.time;
},
Id: Id,
teacher: teacher,
getCurrentSolution: function() {
return math.eval(this.testProblems[this.getCurrentProblemIndex()].problem);
},
getTimeTaken: function() {
return this.getCurrentProblemTimeLimit() - this.time;
},
getCurrentProblemTimeLimit: function() {
return this.testProblems[this.getCurrentProblemIndex()].timeLimit;
},
getCurrentProblemIndex: function() {
return this.testProblems.length - 1;
},
addTestProblem: function(problem, timeLimit) {
var solution = math.eval(problem);
this.testProblems.push({problem: problem, timeLimit: timeLimit, solution: solution});
console.dir(this.testProblems);
},
testProblems: [],
updatePercentages: function(name) {
function round(num) {
return +(Math.round(num + "e+2") + "e-2");
}
console.log('updating percentages');
console.log('answers length ' + this.students[name].givenAnswers.length);
var timeSum = 0;
for(var i = 0; i < this.students[name].givenAnswers.length; i++ ) {
timeSum += this.students[name].givenAnswers[i].takenTime;
}
var timeAvg = timeSum / this.students[name].givenAnswers.length;
console.log('timeAvg for ' + name + ' ' + timeAvg);
this.students[name].avgTime = round(timeAvg);
var correctSum = 0;
for(var j = 0; j < this.students[name].givenAnswers.length; j++ ) {
if (this.students[name].givenAnswers[j].correct) {
correctSum++;
}
}
var correctAvg = correctSum / this.students[name].givenAnswers.length;
console.log('correctAvg for ' + name + ' ' + correctAvg);
this.students[name].avgCorrect = round(correctAvg) * 100;
},
addGivenStudentAnswer: function(name, answer, takenTime, index) {
console.log('adding answer ' + name + ' ' +answer+ ' ' + takenTime);
var correct = this.getCurrentSolution() == answer;
if (typeof this.students[name].givenAnswers[index] === 'undefined') {
this.students[name].givenAnswers[index] = ({
answer: answer,
takenTime: takenTime,
correct: correct
});
this.updatePercentages(name);
//console.dir(this.students[name].givenAnswers);
return true;
} else {
console.log('attempt at double answer. not saved');
return false;
}
},
addStudent: function(name) {
if (!(this.students[name])) {
this.students[name] = {
studentName : name,
avgTime: 0,
avgCorrect: 0,
givenAnswers: []
}
}
console.dir(this);
},
students: {}
};
console.dir(this);
},
deleteRoom: function(Id) {
delete this[Id];
console.log('room deleted from testResults');
}
};
// after test
var name = socket.userName;
var room = socket.room;
var created = testResults[room].created;
var students = testResults[room].students;
var problems = testResults[room].testProblems;
var test = new tests({
roomId : room,
created : created,
teacher : name,
students : students,
problems : problems
});
test.save(function(err, result) {
if (err) {console.log(err);}
else {
console.log('test saved to DB');
socket.emit('testSaved');
// delete from roomList
testRooms.deleteRoom(room, name);
// delete from resultObject
testResults.deleteRoom(room);
// answer
io.in(room).emit('room Closed');
}
});
route for reading a test from DB afterwars
router.get('/showtests/:roomId', function(req, res) {
if (req.user && req.user.role === 'teacher') {
tests.findOne({roomId: req.params.roomId}, {}, function(err, result) {
if (err) {console.log(err);}
res.render('showSingleTest', {user: req.user, testData: JSON.parse(JSON.stringify(result))});
})
} else {
res.render('customerror', { title: "Error", errMsg1: "error.error", errMsg2: "error.notLoggedIn" });
}
});
aaaaaand the jade
h2(data-i18n="markup.studentsAnswers")
each student in testData.students
.testViewSingleStudentAnswers.col-md-6
h3 #{student.studentName}
ol.answers
each answer in student.givenAnswers
if (answer)
if (answer.correct == true)
li.correct
span #{answer.answer}
|
span.floatRight (#{answer.takenTime}s)
else
li.wrong
span #{answer.answer}
|
span.floatRight (#{answer.takenTime}s)
else
li.noAnswer(data-i18n="markup.noAnswerGiven")
.testTotals
| #{student.avgCorrect}
span(data-i18n="markup.percentCorrect")
| ,
| #{student.avgTime}
span(data-i18n="markup.avgTime")
You can do like so:
function push_answer(answer){
answer = answer || "undefined"
array_of_answers.push(answer)
}
Now, the value is not undefined, but it's defined by the literal. You can replace it with some unicode character in case some answer can be "undefined".
Have a nice day!
Seems to be working without issue for me.
HTML:
<div id="content">
</div>
JS:
var answers = ["answer1","answer2",undefined,"answer3",undefined];
for(i=0;i<answers.length;i++) {
if(!answers[i]){
answers[i]="no answer";
}
}
document.getElementById('content').innerHTML = answers;
jsFiddle
My personal recomendation: "Never leave things to chance"
if the student doesn't choose an answer, you should fill that blank space with a '', because undefined is really annoying to handle, so in order to fill the gaps:
This will check if answer is "undefined" and fill the gap with a '' (blank), then, when you check that answer, is going to be more simple to evaluate...
if(answer)
answers[questionindex] = answer;
else
answers[questionindex] = '';

Categories