How do I fix my Javascript function that does not work? - javascript

I am starting to learn Javascript and I do not understand the following:
I need to execute the method "pay" in order to pay all the different persons; so, I need to complete the function "salary". The function receives an array object; all those objects "know" how to execute the method "pay". Also, I want to store the result in the array "result".
I did this but it seems it's not working:
function salary($persons) {
$results= [];
$persons->pay();
return $results;
}
What am I missing? What's wrong with my function?

-> is not Javascript syntax.
To construct one array by performing an operation on each item of another array, use .map:
function salary(persons) {
return persons.map(person => person.pay());
}
function salary(persons) {
return persons.map(person => person.pay());
}
console.log(salary([
{ pay: () => 5 },
{ pay: () => 10 }
]));
Since this isn't PHP, there's no need to prefix variables with $ either.

Related

Can setInterval() be put in the last .then() method in Javascript?

A new javascript question, again, and again...
(Updated)
a is a string where I input a word, it will then be sent to a Dictionary API and do the fetchApi(). After getting the result, if a matches the result[0].word, it will trigger the function Func1(), if not then it will trigger the other function Func2().
What I'd like to ask is there any way to use setInterval() for the function data(result), after deleting the part .then(result => data(result)) in the function fetchApi(a)? Or can I only put setInterval() for both functions Func1() and Func2()?
Thank you very much!
var a = "";
function fetchApi(a) {
let url = `${url}${a}`;
fetch(url).then(res => res.json()).then(result => data(result));
}
function data(result) {
// console.log(result);
if (a == result) {
Func1();
} else {
Func2();
}
}
Putting setInterval() on data() will not do anything because result will never change once it has returned. You need to call the whole fetchApi function again if you're expecting the result to be updated later on.

(Mongo/Mongoose) How to handle waiting for the result of multiple queries

I'm writing a Discord bot that generates weekly Guild stats for text and voice channel usage. My code divides several Mongo queries up into separate methods:
function getTopActiveTextChannels() {
let topTextChannels = []
ChannelModel.find({}).sort({"messageCountThisWeek": -1}).limit(topLimit)
.exec(channels => {
channels.forEach(c => {
topTextChannels.push({"name": c.name, "messageCount": c.messageCount})
})
console.log(topTextChannels)
return topTextChannels
})
}
function getTopActiveVoiceMembers() {
let topVoiceMembers = []
UserModel.find({}).sort({"timeSpentInVoice": -1}).limit(topLimit)
.exec(users => {
users.forEach(u => {
topVoiceMembers.push({"username": u.username, "timeSpentInVoice": u.timeSpentInVoice})
})
console.log(topVoiceMembers)
return topVoiceMembers
})
}
I then have one method that calls both those and (for now) prints the values to console:
function getWeeklyGuildStats(client) {
let topActiveTextChannels = getTopActiveTextChannels()
let topVoiceMembers = getTopActiveVoiceMembers()
let promisesArray = [topActiveTextChannels, topVoiceMembers]
Promise.all(promisesArray).then(values => {console.log(values)})
}
Executing getWeeklyGuildStats(client) outputs: [ undefined, undefined ]. I am sure I'm not using promises correctly, but when I follow Mongoose's documentation, it tells me to use exec() instead of then(), but I get a channels = null error with that.
Does anything jump out to anyone? This seems like a fairly common pattern. Does anyone have a solution for how to resolve multiple Mongoose queries in a single method?
Promise.all should take an array of promises, while your functions are returning normal array, so you need to return the whole query in the helper method which getting the users and channels, then do your logic after the promise.all
your functions may look something like that
function getTopActiveTextChannels() {
return ChannelModel.find({}).sort({"messageCountThisWeek": -1}).limit(topLimit).exec();
}
function getTopActiveVoiceMembers() {
return UserModel.find({}).sort({"timeSpentInVoice": -1}).limit(topLimit).exec();
}
then the function that calls these two methods will be something like
function getWeeklyGuildStats(client) {
let topActiveTextChannels = getTopActiveTextChannels()
let topVoiceMembers = getTopActiveVoiceMembers()
let promisesArray = [topActiveTextChannels, topVoiceMembers]
Promise.all(promisesArray).then(values => {
console.log(values);
// here you could do your own logic, the for loops you did in the helper methods before
});
}
You do not have any return statements in the root level of your functions, so they are always synchronously returning undefined. I'm not familiar with the library you're using, but if for example ChannelModel.find({}).exec(callback) returns a promise with the return value of callback as your code implies, then you just need to add a return statement to your functions.
For example:
function getTopActiveTextChannels() {
let topTextChannels = []
// Return this! (Assuming it returns a promise.) Otherwise you're always returning `undefined`.
return ChannelModel.find({}).sort({"messageCountThisWeek": -1}).limit(topLimit)
.exec(channels => {
channels.forEach(c => {
topTextChannels.push({"name": c.name, "messageCount": c.messageCount})
})
console.log(topTextChannels)
return topTextChannels
})
}

What does .push.bind do?

I'm trying to learn coding in Javascript and came across this question and have a few questions. What does results.push.bind(results) do in this question? Please see question below:
Suppose getData is a function that takes a query object and returns a promise for the result of the query. Suppose also that someArrayOfQueries is an array of query objects. Explain what would be printed by the following code and why:
function runMultipleQueries(queries) {
var results = [];
queries.forEach(doQuery);
return results;
function doQuery(query) {
getData(query)
.then(results.push.bind(results));
}
}
function log(value) {
console.log(value);
}
runMultipleQueries(someArrayOfQueries).forEach(log);
In Javascript, unlike in other Object Oriented languages, the variable this is quite complicated and can be changed many times.
When working with arrays, the function push takes the array it is called "on", and puts a new element to the end of it. In this case, the push function knows what array it is working on by reading the this variable.
Imagine this example code:
var myDataStructure = {};
myDataStructure.value = 0;
function addOneToValue() {
this.value += 1;
}
myDataStructure.addOne = addOneToValue;
myDataStructure.addOne(); // here - the variable `this` == myDataStructure
However, if you call the function addOneToValue with the code addOneToValue(), the value of this is not in fact myDataStructure. It is undefined, or a global object like window.*
To manually force addOneToValue to always use myDataStructure as the this value, you can do the following:
addOneToValue = addOneToValue.bind(myDataStructure);
Now you can safely call addOneToValue(), since the this value was bound to myDataStructure.
In your code, runMultipleQuery is passing the function to another handler, who will not call the function like results.push. That means when the function is called, push will again have an uncertain this value. However, by calling bind, we can ensure that runMultipleQuery calls the push function and forces this to be the results variable.
What results.push.bind(results) does is it forces the method call of push to use the scope of results.
TLDR;
results is an Array. results.push() is a call to Array#push, but by passing the function results.push directly to the Promise Promise#then it will be called within a different scope.
That is, the keyword this will reference a different object.
By using bind (e.g. - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind), the scope will be "bound" to a specific Object.
The reason for this is because JavaScript is a prototype language, rather than an object-oriented language.
What does .push.bind do?
Returns the .length of the array, when called
function runMultipleQueries(queries) {
var results = [];
return Promise.all(queries.map(doQuery));
function doQuery(query) {
return Promise.resolve(query)
.then(results.push.bind(results))
}
}
function log(value) {
console.log(value);
}
runMultipleQueries([10, 20, 30]).then(log);
Note, that as astutely observed and alerted to by #Bergi, the pattern .then(results.push.bind(results)) actually returns the .length of the array.
One solution which still includes the use of the pattern is to chain a second .then() to get the .length returned by previous .then() and return the element of the array by subtracting 1 from the value and using bracket notation
function runMultipleQueries(queries) {
var results = [];
return Promise.all(queries.map(doQuery));
function doQuery(query) {
return Promise.resolve(query)
.then(results.push.bind(results))
.then(len => results[len - 1]);
}
}
function log(value) {
console.log(value);
}
runMultipleQueries([10, 20, 30]).then(log);
though creating results array is not necessary when using Promise.all(), which returns an array of values
.forEach() alone will not await the previous result of getData() call, results will probably be an empty array at runMultipleQueries(someArrayOfQueries).forEach(log);
Use Promise.all() and .map() instead of .forEach(), return the Promise from getData() call
function runMultipleQueries(queries) {
return Promise.all(queries.map(doQuery));
function doQuery(query) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(query)
}, Math.floor(Math.random() * 1000))
})
}
}
function log(value) {
console.log(value);
}
runMultipleQueries([1,2,3]).then(log);

How to make use of recursion in preg_replace_callback

I have a JavaScript function (not plain JavaScript)
function formatString () {
var args = jQuery.makeArray(arguments).reverse();
var str = args.pop();
return str.replace(/{\d\d*}/g, function () {
return args.pop();
});
}
that I use to format a string e.g
formatString("{0} and {1}", "first", "second");
returns "first and second"
I want to replicate the same functionality in php and I have the following function:
function formatString() {
if(func_num_args() > 1) {
$args = array_reverse(func_get_args());
$input = array_pop($args);
}
return preg_replace_callback('/{\d\d*}/', function() use ($args) {
return array_pop($args);
}, $input);
}
However, in this case formatString("{0} and {1}", "first", "second");
returns "first and first"
Unlike the JavaScript version of the function where the callback executes for every match, the php variation seems to only execute the callback once.
I am considering making a recursive call to formatString as the callback but since preg_replace_callback calls the callback with its own arguments (here in array of matches) I am having a challenge with recursion.
Please advise on how best to utilize the callback or any alternative solution to using preg_replace_callback. The use of global variables is out of the question.
Your problem: PHP is pass-by-value. Consider this:
function pop_it($array) {
array_pop($array);
print_r($array);
}
$array = array(1, 2);
print_r($array);
// => Array([0] => 1, [1] => 2)
pop_it($array);
// => Array([0] => 1)
print_r($array);
// => Array([0] => 1, [1] => 2)
Your function is called just fine. But always with the same argument, since $args never changes. Not where it matters - outside the callback. You need pass-by-reference to make the change stick.
Change to function pop_it(&$array) (or use (&$args)), and... something happens.
However, I do not like this approach, as it will do the wrong thing for formatString("{1} and {0}", "second", "first"), as well as for formatString("{0} and {0}", "only"). I'd just pluck the index out from the regexp and find it in the array. (Also, \d\d* is less legible than, but equivalent to, \d+.)

Is map() in javascript synchronous?

Function is :
[1,2,3].map( function (item)
{
console.log(item);
//return 'something';
});
My expected behaviour is getting only 1 as output, unless i uncomment the
//return 'something'
But i really get
1
2
3
What am i doing wrong ?
UPDATE:
i am testing that with nodejs.
i really dont understand.
var async = require("async");
[1,2,3].map( function (item)
{
console.log(item);
//return 'something';
});
async.map([1,2,3], function (item,callback)
{
console.log(item);
//callback(null,true)
}, function (err,result)
{
console.log(result);
}
);
Both return the same
1
2
3
And i really would like to wait till i get a return or a callback till the next item is executed.
SOLVED
async.mapSeries([1,2,3], function (item,callback)
{
console.log(item);
//callback(null,true)
}, function (err,result)
{
console.log(result);
}
);
is the way to do it.
Yes, map is synchronous.
It's a higher order function, that takes a new function and applies it to the given array.
Some people think that because they give a function as a parameter to map then it 'should' act like an event callback function, but it really doesn't. The map function just applies the function parameter to the array and only after it finishes, it continues execution for the resulting code after the map block.
As to your 'expected behavior' - it just doesn't work like you think ;)
"The map() method creates a new array with the results of calling a provided function on every element in this array."
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
The callback is called for each item, your logic is executed and the return value is set as an item in the new array.

Categories

Resources