Callback that didn't need parameters - why? - javascript

So, I have been working on this 2-part problem and managed to solve it, however I have some questions on how the code actually works - specifically the nature of the callback function. Below are the 2 parts of the problem laid out, along with my solution to both. I was able to solve the first part rather easily, but the second was much more challenging for me.
// FIRST PART OF PROBLEM
var merge = function(array1, array2, callback){
//your code here.
}
var x = merge([1, 2, 3, 4], [5, 6, 7, 8], function(a, b){
return a + b;
});
//x should now equal [6, 8, 10, 12].
// MY SOLUTION:
var merge = function(array1, array2, callback){
var newArray = [];
for (var i = 0; i < array1.length; i++) {
newArray[i] = callback(array1[i], array2[i]);
}
return newArray;
}
var x = merge([1, 2, 3, 4], [5, 6, 7, 8], function(a, b){
return a + b;
}); // x equals [6, 8, 10, 12].
Now here is the second part of the problem that I was able to solve, but where I am having trouble is in specifically walking through the code step by step. My question is - why did I not need to give the callback parameter to the merge function any parameters? When I did attempt to provide parameter, x became NaN.
euclidian distance = sqrt((x2-x1)^2 + (y2-y1)^2))."
var euclid = function(coords1, coords2){
//Your code here.
//You should not use any loops and should
//instead use your original merge function.
}
var x = euclid([1.2, 3.67], [2.0, 4.4]);
//x should now equal approximately 1.08.
// My solution:
var euclid = function(coords1, coords2){
// // why does function() below require no parameters?
var merged = merge(coords1, coords2, function() {
return (coords1[0] - coords2[0]) * (coords1[0] - coords2[0]) +
(coords1[1] - coords2[1]) * (coords1[1] - coords2[1]);
});
return Math.sqrt(merged[0]);
};
var x = euclid([1.2, 3.67], [2.0, 4.4]); //x does equal approximately 1.08.
I'm a newbie to programming, so I realize this is probably a rather trivial question. But any help with walking me through how the callback is actually working in this solution would be much appreciated. Thanks!

why did I not need to give the callback parameter to the merge function any parameters?
Given:
var euclid = function(coords1, coords2){
// why does function() below require no parameters?
var merged = merge(coords1, coords2, function() {
return (coords1[0] - coords2[0]) * (coords1[0] - coords2[0]) +
(coords1[1] - coords2[1]) * (coords1[1] - coords2[1]);
});
return Math.sqrt(merged[0]);
};
The (callback) function passed to the merge function has a closure to the variables of its outer scope. Including coords1 and coords2 in the formal parameter list of Euclid makes them local variables, so they don't need to be passed to the callback.
If the function was created outside the scope of Euclid, then you'd need to pass them (or reference them some other way).
Incidentally, I prefer function declarations to assignment of expressions, e.g.
function euclid(coords1, coords2) {
// code here
}
as it makes it more obvious what the function's name is. Consider:
var foo = function fred() {
// foo or fred in here
};
// only foo out here

The second solution can be said to use merge only trivially. It's like that old question - how do you measure height of a building using an ammeter and a stopwatch? Throw the ammeter from the roof, measure time till pavement.
Your merge function will invoke its callback twice, the callback will calculate the full Euclid distance for the two-size arrays, and it will do it twice (so you'll get [1.08, 1.08] for the result of the merge; then you pick one of the results.
So your merge is a) unnecessary, b) inefficient, and c) doesn't work for any number of elements but 2.
The real solution involves noticing that Euclid distance is a sum; and each element of the sum only involves matching elements from two arrays: a[i] - b[i] squared. That should be your callback; and merge should give you an array like [0.64, 0.53].
The real problem is - how do you sum an array when you can't use loops, only your merge? If you are guaranteed that there will be only two dimensions, it is easy. If not, you will have to cheat.
var euclid = function(aa, bb) {
var squares = merge(aa, bb, function(a, b) {
return (a - b) * (a - b);
});
return Math.sqrt(squares.reduce(function(a, b) {
return a + b;
}));
};
Look, ma, no loops! (At least no overt ones.)
If you really want to only use merge, you will need to cheat harder.
var euclid = function(aa, bb) {
var sum = 0;
var squares = merge(aa, bb, function(a, b) {
sum += (a - b) * (a - b);
return null;
});
return Math.sqrt(sum);
};

In my opinion you're getting confused because your callback is not a callback and thus shouldn't be named this way. You rather wanna name it resolver, or calculator: the function one need to provide is not intended to be called back right after the process, but rather to be called while resolving, thus the naming concern. The pattern of providing a calculator function parameter is when you want to give the ability of customizing your function behavior to the caller.
So, the only reason they didn't tell you to make the euclid function accept a resolver parameter is... well, they didn't want you to create a behavior-customizable function. That simple =D

Related

Understanding javascript borrowing methods

There is a lot of explanation about how to convert a function's arguments to a real array.
But I have found it very interesting when you simplify the code with the help of bind.
MDN Array.prototype.slice - Array-like objects
MDN Function.prototype.bind - Creating shortcuts
For example:
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
Simplified call:
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
function list() {
return slice(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
It is working the same way if you use apply instead of call:
var slice = Function.prototype.apply.bind(unboundSlice);
Which can be even shortened by using call from any function instance, since is the same as the one in the prototype and same approach with slice from an array instance:
var slice = alert.call.bind([].slice);
You can try
var slice = alert.call.bind([].slice);
function list() {
console.log(slice(arguments));
}
list(1, 2, 3, 4);
So the first very weird thing is coming into my mind is calling bind on apply, but the first argument of bind should be an object (as context) and not a function (Array.prototype.slice).
The other is that is working with both call and apply the same way.
I am writing javascript for quite a long time and using these methods day to day confidently but I can not wrap my head around this.
Maybe I am missing some very fundamental detail.
Could somebody give an explanation?
the first argument of bind should be an object (as context)
Yes.
and not a function (Array.prototype.slice).
Why not? For one, all functions are objects, so nothing wrong here.
From another perspective, if you use slice.call(…) or slice.apply(…) then the slice object is the context (receiver) of the call/apply method invocations.
What is the difference between binding apply and call?
There is no difference between applyBoundToSlice(arguments) and callBoundToSlice(arguments). The difference is applyBoundToSlice(arguments, [0, n]) vs callBoundToSlice(arguments, 0, n) if you want pass start and end arguments to the slice.
One can use call keyword to implement method borrowing. Below example shows method borrowing.
var john = {
name: "John",
age: 30,
isAdult: function() {
console.log(this.name+"'s age is "+this.age);
if (this.age > 18) {
return true;
} else {
return false;
}
}
}
console.log(john.isAdult());
var rita = {
name: "Rita",
age: 15
}
console.log(john.isAdult.call(rita));
Observe the last line. How the keyword call is used and how rita object is passed to the function.

Loop logic for drawing line javascript

I have following two arrays:
var element_1 = new Array([x1,y1],[x2,y2],[x3,y3],[x4,y4]);
var element_2 = new Array([x1,y1],[x2,y2],[x3,y3],[x4,y4]);
Logic:
I want to run a loop (nested) where each element of element_1 (for eg [x1,y1]) is compared to each element of element_2 and the shortest distance between them shall be calculated within the loop (I know how to calculate the shortest path). The tricky part here is that I need a reference that which pair made the shortest past and then obtain those [x1,y1] and [x2,y2] combinations to draw a line.
Sample data:
var element_1 = new Array([10,0],[20,10],[10,20],[0,10]);
var element_2 = new Array([10,30],[20,40],[10,50],[0,40]);
Line should be made between [10,20] and [10,30]. Also, I would somehow need to store the coordinates somewhere to pass it to the line drawing function
How can I do this? Any leads would be highly appreciated.
Here is how I would do it:
var element_1 = [[0,0],[1,2],[5,3],[6,8]];
var element_2 = [[0,1],[1,4],[5,9],[9,8]];
var closest = {a: false, b: false, distance: false};
for(var i=0; i<element_1.length; i++) {
for(var j=0; j<element_2.length; j++) {
var distance = calculate_distance(element_1[i], element_2[j]);
console.log('Distance between element_1['+i+'] and element_2['+j+']: ' + distance);
if(closest.distance === false || distance < closest.distance) {
closest = {a: element_1[i], b: element_2[j], distance: distance};
}
}
}
console.log('The shortest path is between '+closest.a+' and '+closest.b+', which is '+closest.distance);
function calculate_distance(a, b) {
var width = Math.abs( a[0] - b[0] ),
height = Math.abs( a[1] - b[1] ),
hypothenuse = Math.sqrt( width*width + height*height );
return hypothenuse;
}
As Roko C. Buljan said, in your case you can just replace new Array() with []. Here's why.
Well i liked this question a lot. It inspired me to invent a generic Array method to apply a callback with each other items of two arrays. So i called it Array.prototype.withEachOther(). What it does is exactly what #blex has done in his solution with nested for loops. It applies an operation (provided by the callback) to each array item with the other array's item. Let's see how it works.
Array.prototype.withEachOther = function(a,cb,s=0){
return this.reduce((p,et) => a.reduce((q,ea) => cb(et,ea,q),p),s);
};
var element_1 = [[10,0],[20,10],[10,20],[0,10]],
element_2 = [[10,30],[20,40],[10,50],[0,40]],
cb = (p1,p2,q) => {var h = Math.hypot(p1[0]-p2[0],p1[1]-p2[1]);
return h < q.d ? {d:h,p1:p1,p2:p2} : q},
minDist = element_1.withEachOther(element_2,cb,{d:Number.MAX_SAFE_INTEGER,p1:[],p2:[]});
console.log(minDist);
So let's explain what's going on.
Array.prototype.withEachOther = function(a,cb,s=0){
return this.reduce((p,et) => a.reduce((q,ea) => cb(et,ea,q),p),s);
};
is a reusable function. It will execute the operation that is provided in a callback function, with each other element of the two arrays. It takes 3 arguments (a,cb,s=0).
a is the second array that we will apply our callback to each item for each item of the array that is invoking .withEachOther.
cb is the callback. Below I will explain the callback applied specific for this problem .
s=0 is the initial (with a default value of 0) value that we will start with. It can be anything depending on the callback function.
return this.reduce((p,et) => a.reduce((q,ea) => cb(et,ea,q),p),s);
this part is the core of the function. As you see it has two nested reduces. The outer reduce has an initial value designated by the s, which is provided as explained above. The initial value gets initially assigned to the p argument of the outer reduce's callback and the other argument et is assigned one by one with each of the items of invoking array. (element of this). In the outer reduce we invoke another reduce (the inner reduce). The inner reduce starts with the initial value of the result of previous loop which is the p of outer reduce and after each calculation returns the result to it's reduced value variable q. q is our memory and tested in the callback to see if we keep it as it is or replace it with the result of our calculation. After inner reduce finishes a complete round it will return the q to p and the same mechanism will run again until we finish with all items of the array that's invoking .withEachOther.
cb = (p1,p2,q) => {var h = Math.hypot(p1[0]-p2[0],p1[1]-p2[1]);
return h < q.d ? {d:h,p1:p1,p2:p2} : q}
The callback is special to this problem. It will receive two points (each with x and y coordinates) Will calculate the distance between them and will compare it with the previously made calculation. If it's smaller it will replace q by returning this new value; if not it will return q as it is.

Range using recursion, how to output a new array Javascript

How do you output a new array using recursion without declaring an empty array outside of the function? Another way of doing it will be creating an inner function and then return newFunction(), but it is not allowed as the task is to call the function itself. Here's what I have so far:
var newArr=[];
var range = function(x, y) {
if(x === y-1){
return newArr;
}
if(x < y){
newArr.push(x+1);
newArr = range(x+1,y);
}
else{
newArr.push(x-1);
newArr = range(x-1,y);
}
return newArr;
};
range(2,10) //[3,4,5,6,7,8,9]
So the key to this kind of thinking is understanding that you should be creating a lot of arrays.
Looking at a slightly different example...
A factorial is a number which goes backwards, through positive integers, multiplying each term with the term below it, and is written like 5!.
These are helpful when you find yourself asking questions like:
"How many permutations of ____ are there?"
"Given these 5 things, how many permutations can I arrange them in, from left to right?"
5! // =>
5 x 4 x 3 x 2 x 1 // =>
120
You could see how we could build a loop and set a variable for a counter, and a variable for the total, and multiply the current total by the current value of the counter we're decrementing.
But instead of doing that, we can try to use recursion.
First, think about how we could simplify that 5 x 4 x ... into one repeated step.
Really, 2! is 2 x 1. 3! is 3 x 2 x 1, which happens to be 3 x 2!.
So the general case might be something like: n! == n x (n - 1)!
So I might write a generalized function which does something like this:
// DO NOT RUN THIS FUNCTION!
function factorial (n) {
return n * factorial(n - 1);
}
So if I run factorial(5) and use my imagination, we can see that the program is doing something like:
factorial(5)
=> return 5 * factorial(5-1)
=> return 4 * factorial(4-1)
=> return 3 * factorial(3-1)
=> ...
Can you see any problems with the function as-is?
I said at the beginning that factorials (in this simplified case) are over positive integers.
How does my function know to stop when the integers stop being positive?
It doesn't, currently. Which is why the above implementation attempts to run forever, and will freeze the browser, while it tries to, until it gets thousands or tens of thousands of functions deep, before it says that you've reached the maximum depth of the call stack and explodes.
What we really need is a condition or a set of conditions, which we use to determine when we're done.
This is a base-case.
if (shouldStop(n)) {
return defaultValue;
}
Or in our case:
function factorial (n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
Now, when we run the function, we have:
factorial(5)
=> 5 * factorial(5 - 1)
=> 4 * factorial(4 - 1)
=> 3 * factorial(3 - 1)
=> 2 * factorial(2 - 1)
=> 1
=> 2 * 1
=> 3 * 2
=> 4 * 6
=> 5 * 24
=> 120
This is recursion.
And because of where the call is (returned at the very end of whatever branch you're in) it's a special kind of recursion (tail recursion), which allows some languages to optimize the code, replacing the function call with the contents of the function call, and thus skip adding to the call-stack like the first version (future versions of JS will support this power).
In more modern JS, I might rewrite it to look something like
const factorial = n => n <= 1 ? 1 : factorial(n - 1);
So now, what about other cases?
Well, sometimes, you need to make sure you're passing more things in.
Think about what your problem is, and what kinds of counters or flags or collectors you need, in order to do your job.
Here's one:
function makeNumberString (current, max, initialString) {
var str = initialString || ""; // maybe I don't have one yet
var currentString = str.concat(current.toString());
if (current > max) {
return initialString;
}
return makeNumberString(current + 1, max, currentString);
}
makeNumberString(0, 9); // "0123456789"
There are other ways of filling that function out, to make it do the same thing.
Note that currentString there is always a brand new string, made by joining the string that I was given with the new value I was passed. I'm not actually modifying the original string, but creating a new copy [HINT!!].
I hope that helps you.
you can simply do like this;
var range = (x,y,a=[]) => (++x < y && (a = range(x,y,a.concat(x))),a),
arr = range(2,10);
console.log(arr);
Note that the returned array is a parameter of the function and is passed to successive recursive calls.
There are many ways to skin this cat.
The simple way: create an array with the first value in it, then
concatenate the remaining values to it.
var range = function(x,y){
return x+1 >= y ? [] : [x+1].concat(range(x+1, y));
}
console.log(JSON.stringify(range(1, 10)));
The array is being constructed from right to left. Notice how the
recursive call to range is not the last thing the function does
before it returns: concatenation of the array follows.
We can also rewrite the function to be tail recursive with an accumulator as a parameter.
var range2 = function(x,y,a){
a = a || [];
return x+1 >= y ? a : range2(x+1, y, a.concat(x+1));
}
console.log(JSON.stringify(range2(1, 10)));
Now the call to range2 is the last thing the function does before
it returns. ES6 compliant JS engines are required to
optimise
calls in tail position (in strict mode) by discarding the execution
context from the stack.
Notice how we're now constructing the array from left to right.
You can avoid the extra parameter by using a helper function.
I've used an inner function, but it doesn't have to be.
var range3 = function(x,y){
var r = function(x,y,a){
return x+1 >= y ? a : r(x+1, y, a.concat(x+1));
}
return r(x, y, []);
}
console.log(JSON.stringify(range3(1, 10)));
Tail recursive using continuation passing style.
var range4 = function(x,y){
var r = function(x,y,c){
return x+1 >= y ? c([]) : r(x+1, y, function(a){
return c([x+1].concat(a));
});
}
return r(x, y, function(a){return a;});
}
console.log(JSON.stringify(range4(1, 10)));
Notice the similarity with the original range: the array is
constructed in reverse. This is trickier to get your head around and
may be something you never need, but it doesn't hurt to be aware of
it.
Try this:
function rangeRecursive(start, end) {
if(start === end){
return end;
} else if(start > end){
return [];
} else {
return [start].concat(rangeRecursive(++start, end));
}
}
console.log(rangeRecursive(4, 15));

Javascript syntax with functions

I'm not sure what this means or how to word the question correctly.
In the first example I could put the variable before the function "numbers1.forEach(...)" but in the second example I could not "num.square()"?
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
var numbers2 = [1, 2, 3, 4, 5], sum = 0;
forEach(numbers2, function(number) {
sum += number;
});
console.log(sum);
// → 15
var numbers1 = [1, 2, 3, 4, 5], sum = 0;
numbers1.forEach(function(number) {
sum += number;
});
console.log(sum);
// → 15
// this works!
But this does not work
var square = function(x) {
return x * x;
};
var num = 12;
console.log(square(num));
// → 144
console.log(num.square());
// → TypeError: undefined is not a function (line 9)
// does not work?
Thanks!
Syntax-wise, there's no "magic" happening here. You're describing two very different things.
In your first example, you're passing an Array to your own defined forEach method. In the second example, the forEach you're calling is actually a built-in method on Arrays (see MDN). If you changed your forEach to not invoke action, you'll see that yours breaks and the second one still works.
You can achieve this functionality yourself. You could add a function to Number to make your second example work (although this is not a recommended practice, as it could collide with future JavaScript features [and many other reasons] or break existing functionality):
Number.prototype.square = function() {
return this * this;
};
var num = 12;
var squared = num.square();
alert(squared);
If you remove the forEach function from your first example, it will still work, that is because the Array type has a builtin forEach function.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
Alternatively try to change your forEach function to each and you will see that it will no longer work.
What you are trying to do is add a new method to an existing type. This is not good practice in JavaScript.
The example with the array works because Array in JavaScript (ECMAScript5) defines a forEach function.
The example with the number doesn't work because Number doesn't define a square function. You can extend Number to include the square function like this:
Number.prototype.square = function(x) {
return this * this;
};

javascript equivalent of php call_user_func

I've found this topic which I've implemented (see accepted answer):
javascript equivalent of PHP's call_user_func()
However, I am having a problem with multiple parameters. I realize what I was doing was turning my parameters into strings and treating it like 1 parameter, but I don't know how to fix this because I am dynamically creating the parameters.
Meaning, I have defined in my code the following:
var a = new Array();
a[0] = new Array();
a[0][0] = 'alert';
a[0][1] = '\'Hello World\'';
a[1] = new Array();
a[1][0] = 'setTimeout';
a[1][1] = 'alert("goodbye world")';
a[1][2] = '20';
Later, I was calling them like this:
var j = 0;
var len = 0;
var fx = '';
var params = '';
for( i in a ){
params = '';
len = a[i].length;
fx = a[i][0]; // getting the function name
a[i].splice( 0, 1 ); // removing it from array
if( len > 1 ){
params = a[i].join(", "); // trying to turn the parameters into the right format, but this is turning it into strings I think
params = params.replace(/\\'/g,'\''); // bc i was adding slashes with PHP
}
window[fx](params);
}
I don't have to use arrays to do this. I don't understand JS OOP (haven't tried yet), though I am comfortable with PHP OOP, so I don't know if there is a way to do this there.
Any help on passing multiple parameters would be appreciated.
Thanks.
First thing to do: Scrap your entire code, start over. Your approach will not get you anywhere where you'd want to be. (Unfortunately I can't tell you where you'd want to be because I cannot make sense of your example.)
There are three ways to call a function in JavaScript.
function foo() { console.log(arguments); }
// 1. directly
foo(1, 2, 3);
// 2. trough Function.call()
foo.call(this, 1, 2, 3);
// 3. trough Function.apply()
var args = [1, 2, 3];
foo.apply(this, args);
call and apply are similar. They let you decide which object the this keyword will point to inside the function (that's the important bit!).
apply accepts an array of arguments, call accepts individual arguments.
The closest thing to call() is PHP's call_user_func(). The closest thing to apply() is PHP's call_user_func_array().
JavaScript objects share something with PHP arrays: They are key/value pairs.
// an anonymous function assigned to the key "foo"
var obj = {
foo: function () { console.log(arguments); }
};
This means you can access object properties either with the dot notation:
// direct function call
obj.foo(1, 2, 3);
Or through square bracket notation (note that object keys are strings):
var funcName = "foo";
obj[funcName](1, 2, 3);
obj[funcName].call(obj, 1, 2, 3);
obj[funcName].apply(obj, [1, 2, 3]);
Square bracket notation gives you the freedom to choose an object property dynamically. If this property happens to be a function, apply() gives you the freedom to choose function arguments dynamically.
Every top-level function that has not been declared as the property of some object will become the property of the global object. In browsers the global object is window. (So the function foo() in my first code block above really is window.foo.)
Note that this does not work like in PHP. It will point to the object the function has been called on, not the object the function "belongs to". (The concept "belongs to" does not really exist in JavaScript. Things can be modeled that way, but it's only a convention.)
With direct calling (obj.foo(1, 2, 3)), this will point to obj. With call and apply, this will point to whatever object you want to. This is a lot more useful than it sounds at first. Most of the time when you want to call functions dynamically, you will end up using apply.
Check out Function.apply:
function test(a, b) { console.log([a, b]) }
test.apply(null, [1, 2]); // => [ 1, 2 ]
Late to the party, but now with ES6 you can simply do
function FunctionX(a,b,c,d){
return a + b + c + d;
}
let fx = "FunctionX";
let params = [ 1, 10, 100, 200 ];
let answer = window[fx]( ... params);
let answer2 = globalThis[fx]( ... params ); // this is more cross-platform
to unpack your argument array

Categories

Resources