Why isn't my callback on the array working? - javascript

I wrote the code to iterate through a given array and then invoke the function subtractTwo on each element. However, when I run the function map the array argument returns unchanged. Does anyone know why this might be?
// create the subtractTwo function, callback function
function subtractTwo (number) {
return number - 2;
}
// create higher-order function, map
function map (array, callback) {
// initiate a for loop, looping through the array and invoking the callback on each element;
for (let i = 0; i < array.length; i ++) {
callback(array[i]);
}
//return the new array
return array;
}
//console.log(subtractTwo(4));
// Uncomment these to check your work!
console.log(typeof subtractTwo); // should log: 'function'
console.log(typeof map); // should log: 'function'
console.log(map([3,4,5], subtractTwo)); // should log: [ 1, 2, 3 ]```

Related

Memoization: can the arguments be used as key in the cache object?

I have this solution for a memoization function.
const slice = Array.prototype.slice
function memoize(fn){
const cache = {}
return (...args) => {
const params = slice.call(args)
console.log(params)
if(cache[params]){
console.log('cached')
return cache[params]
} else{
let result = fn(...args)
cache[params] = result
console.log('not cached')
return result
}
}
}
cache[params] is the point. cache is an object and params is an array. Will this always work? After some researching I am still not confident this code is correct.
Such memoization can work for very simple cases, but it does not work in many other cases, for instance when:
the arguments are objects. They will often stringify to "[object Object]", and so different objects are treated as if they are the same.
the arguments are strings but cannot be distinguished because of the poor way they are separated when the arguments array is stringified (comma delimiter)
Some demos of failures:
Arguments are Objects
const slice = Array.prototype.slice
function memoize(fn){
const cache = {}
return (...args) => {
const params = slice.call(args)
if(cache[params]){
return cache[params]
} else{
let result = fn(...args)
cache[params] = result
return result
}
}
}
// The function we will test with:
function sum(a) {
let total = 0;
for (let value of a) total += value;
return total;
}
function test() {
console.log(sum(new Set([1,2,3]))); // should be 6
console.log(sum(new Set([2,4,6]))); // should be 12
}
console.log("Run test without memoization...");
test(); // Perform test without memoization
sum = memoize(sum); // Memoize the function
console.log("Run test WITH memoization...");
test(); // Test again, but now with memoization
Arguments are strings that have commas:
const slice = Array.prototype.slice
function memoize(fn){
const cache = {}
return (...args) => {
const params = slice.call(args)
if(cache[params]){
return cache[params]
} else{
let result = fn(...args)
cache[params] = result
return result
}
}
}
// The function we will test with:
function compareString(a, b) {
return a.localeCompare(b); // returns -1, 0 or 1.
}
function test() {
console.log(compareString("b,a", "c")); // should be -1
// Change the arguments such that the concatenation with comma remains the same
console.log(compareString("b", "a,c")); // should be 1
}
console.log("Run test without memoization...");
test(); // Perform test without memoization
compareString = memoize(compareString); // Memoize the function
console.log("Run test WITH memoization...");
test(); // Test again, but now with memoization
Other remarks on the code
Calling slice is useless, as args is already a new array.
if(cache[params]) will evaluate to false when the already cached value is a falsy value, like 0, "", false. Doing if (params in cache) would avoid that issue
params will be stringified, which (as shown above) is not guaranteed to uniquely identify an array of arguments.
Improvement
If we can require that the arguments passed to our function are immutable, then we can use these immutable values or references as keys in a Map.
This Map would become a tree of Maps when there are multiple arguments, so that when a lookup is made for the first argument in the main Map, it returns a nested Map, and then in that Map the next argument would be used as key, ...etc, until the deep Map is found for the last argument. In that final Map, a reserved key is used to retrieve the cached value. This reserved key can be Symbol that is only known by the memoize function, so that it can never collide with an argument value.
Disclaimer: This will not work when objects can mutate between calls.
Here is how that looks:
function memoize(fn){
const end = Symbol("end"); // A unique reference, only known here
const cache = new Map;
return (...args) => {
let node = args.reduce((node, arg) => {
if (!node.has(arg)) node.set(arg, new Map);
return node.get(arg);
}, cache);
if (!node.has(end)) node.set(end, fn(...args));
return node.get(end);
}
}
// The function we will test with:
let numCalls = 0;
function length(a) { // Length of a linked list
numCalls++; // Keep track of the number of calls made
return a ? length(a.next) + 1 : 0;
}
function test() {
numCalls = 0; // Reset the number of calls
let head = { next: { next: { next: {}}}}; // Linked list with 4 nodes
console.log(length(head)); // should be 4
// Now exclude the head node:
console.log(length(head.next)); // should be 3
console.log("number of calls: ", numCalls);
}
console.log("Run test without memoization...");
test(); // Perform test without memoization
length = memoize(length); // Memoize the function
console.log("Run test WITH memoization...");
test(); // Test again, but now with memoization
Again, this cannot be used when objects mutate between calls. But for all other scenarios it should work fine.
Object keys are supposed to be string/symbols.
Depending on how you input array affects the output, you can .join() the array and use it as your cache key:
const slice = Array.prototype.slice
function memoize(fn){
const cache = {}
return (...args) => {
const params = slice.call(args)
let paramsString = params.sort().join('');
console.log(paramsString)
if(cache[paramsString]){
console.log('cached')
return cache[paramsString]
} else{
let result = fn(...args)
cache[paramsString] = result
console.log('not cached')
return result
}
}
}
If the order does not matter then you can .sort(). If order is important then you can remove the sort and just join. This is not perfect but works for simple cases.

In JavaScript, how to execute next function from an array of functions

I have an array of functions, as in:
funcArray = [func1, func2, func3];
When in a given function, I want to execute the next function in the array. How do I do this? Here is my basic skeleton:
function func1() {
// I get current function caller
var currentFunc = func1.caller;
// I want to execute the next function. Happens to be func2 in the example.
}
I cannot use indexOf function, as one would for an array of strings or numbers.
NOTE: This question appears to be similar to this and the one it refers to. However, it is a different question.
I want to alter the sequence of processing by merely modifying the array. That's the goal. A possibly more efficient approach would be appreciated.
Clarification: Based upon some of the comments:
funcArray is global.
The goal is to implement middleware for a Node.js HTTP module in as simple and efficient a manner as possible without using any third-party modules.
Unless func1 closes over funcArray, you cannot have it reach out and find func2 and execute it, nor should you. Even if func1 does close over funcArray, it would be poor separation of concerns for func1 to reach out and find itself in funcArray and then execute func2.
Instead, have other code that's in charge of running the functions.
If they're synchronous
If the functions complete their work synchronously, then it's simply:
funcArray.forEach(fn => fn());
or
for (const fn of funcArray) {
fn();
}
or if the result of one function should be passed to the next, you can use reduce:
const finalResult = funcArray.reduce((previousResult, fn) => fn(previousResult), undefined);
...where undefined is the value to pass to func1.
If they're asynchronous
If they don't do their work synchronously, you'll need to provide them a way to notify their caller that they've completed their work. Promises are a good, standard way to do that, but you could use simple callbacks instead.
If you make them return promises, for instance, you can use the old promise reduce trick:
funcArray.reduce((p, fn) => {
return p.then(() => {
fn();
});
}, Promise.resolve());
or if the result of one function should be passed to the next:
funcArray.reduce((p, fn) => {
return p.then(fn);
}, Promise.resolve());
You can provide an argument to Promise.resolve to set the value to pass to func1 (without one, it'll receive undefined).
You can bind to the function the index where it is in the array so you can use this index to get and call the next function:
var funcArray = [func1, func2];
var boundFuncArray = funcArray.map((f, i) => f.bind(null, i));
boundFuncArray[0]();
function func1(nextFunctionIndex) {
console.log('func1 called');
// Execute next function:
var nextFunc = boundFuncArray[nextFunctionIndex + 1];
nextFunc && nextFunc();
}
function func2(nextFunctionIndex) {
console.log('func2 called');
// Execute next function:
var nextFunc = boundFuncArray[nextFunctionIndex + 1];
nextFunc && nextFunc();
}
As T.J Crowder stated in the comment below, you can also bind the next function to the current one:
var funcArray = [func1, func2];
var boundFuncArray= funcArray.map((f, i, arr) => f.bind(null, arr[i + 1]));
boundFuncArray[0]();
function func1(nextFunc) {
console.log('func1 called');
// Execute next function:
nextFunc && nextFunc();
}
function func2(nextFunc ) {
console.log('func2 called');
// Execute next function:
nextFunc && nextFunc();
}
You can get the current function's name with arguments.callee.name, loop through the array of functions, and call the next function:
funcArray = [func1, func2, func3];
// Only func1() and func2() will be documented since the others have repeating code
function func1() {
// show the current function name
console.log(arguments.callee.name);
// loop the array of functions
for(var i = 0; i < funcArray.length; ++i)
{
// when the current array item is our current function name and
// another function exists after this then call it and break
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func2() {
console.log(arguments.callee.name);
// some logic which switches our next function to be func4()
funcArray[2] = func4;
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func3() {
console.log(arguments.callee.name);
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func4() {
console.log(arguments.callee.name);
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
// call the first function
funcArray[0]();
Output:
func1
func2
func4
I have solved it this way:
// Adding next options to array
function addNext(array) {
array.last = 1
Object.defineProperty(array, 'next', {get:
function() {
if(this.last < this.length) {
this.last++
return this[this.last-1]
} else {
this.last = 1
return () => {}
}
}
});
}
// The functions for array (has to be function and not arrow function)
function first(param) {
console.log('first',param)
return this.next(param)
}
function second(param) {
console.log('second',param)
return this.next(param)
}
function third(param) {
console.log('third',param)
return this.next(param)
}
// The array
let fns = [first,second,third]
// Adding next option to array
addNext(fns)
// Run first function from array
fns[0]('test')
I dont know if your functions require certain parameters but this is the first thing that came to my mind.
var functArray = [
function() {
console.log("function1 executed");
},
function() {
console.log("function2 executed");
},
function() {
console.log("function3 executed");
},
function() {
console.log("function4 executed");
}];
functArray.forEach(function(x){
x();
});
The accepted answer and other comments did help me, but the way I implemented it is as follows:
//The functions are defined as variables.
//They do not get hoisted, so must be defined first.
func1 = function (arg1, arg2) {
//Code to do whatever...
...
//Execute the next function.
//The name of the function is returned by executing nextFunc()
global[nextFunc()](arg1, arg2, arg3);
}
func2 = function (arg1) { //Note different type of args
...
}
//Note that this is an array of strings representing function names.
funcArray = ["func1", "func2", "func3",...]
//Start the execution...
func1(arg1, arg2);
function nextFunc() {
var currentFuncName = nextFunc.caller.name;
var index = funcArray.indexOf(currentFuncName);
if (index < funcArray.length)
return funcArray[index+1];
}
The sequence of functions to be executed is easily managed through the array funcArray. The number or type of arguments is not fixed for each function. Additionally, the functions control if they should stop the chain or continue with the next function.
It is very simple to understand requiring basic Javascript skills. No overheads of using Promises.
"global" gets replaced by "window" for browser. This is a Node.js implementation. The use of function names in the array will, however, break if you minify the JS code. As I am going to use it on the server, I do not expect to minify it.
You can do it in this way with promise.all if your functions to be executed in parallel.
let toBeExecutedList = [];
toBeExecutedList.push(() => this.addTwoNumber(2, 3));
toBeExecutedList.push(()=>this.square(2));
And Then wherever you want to use them, do it like this:
const resultArr = await Promise.all([
toBeExecutedList.map(func => func()),
]);

Implementing JS native 'filter' method with a function that takes two arguments

In a functional programming exercise I found online, the instructions are as follows:
"Implement JavaScript’s native ‘filter’ method that takes two arguments (a collection and a test function that returns either a ‘true’ or a ‘false’) and iterates over the collection using the ‘each’ function you wrote earlier and returns the resultant array."
I've already completed the first part of the exercise and created a function that implements JS' forEach method:
var each = function(collection, iterator) {
if(Array.isArray(collection)){
for(var i = 0; i < collection.length; i++){
// value, key/property, collection
iterator(collection[i],i,collection);
}
} else if (typeof collection === "object"){
for(var property in collection){
iterator(collection[property], property, collection);
}
}else{
console.log("you messed up!");
}
};
Test my function with:
function returnOdds(currentEl) {
return currentEl % 2 !== 0;
}
console.log(filter([1, 2, 3], returnOdds)); // output: 1, 3
I'm not sure how to call my 'each' function on the 'collection' parameter inside my filter function.
Is this legal to do?
function filter(collection, test) {
each(collection);
}
Or perhaps I can call the 'test' parameter as a function that checks to see if collection[i] is not an even/odd number?
function filter(collection, test) {
var odd = function(each()){
each(collection){
if(collection[i] !== 0){
return odd;
}
}
}
}
I am wondering if any of this even makes sense or can be done.
The filter() function that you are being asked to implement needs to somehow build up a new array of values that pass the test, and then return that new array. Here's one way to do it that uses your existing each() method:
var filter = function(collection, test) {
var results = [];
each(collection, function(val) {
if (test(val)) {
results.push(val);
}
});
return results;
};
You can then call this function in the way you showed in the middle of your question, passing in an array and a reference to some other function that will return true or false for a given item:
var oddNumbers = filter([1, 2, 3], returnOdds);
Demo: https://jsfiddle.net/4rj7phot/
So this works as follows:
Create an empty array for the results.
Use your each() function to execute an anonymous function for each item in the collection argument.
Within the anonymous function, call the function provided in the test argument, passing the current item value, and if that function returns true (or a truthy value) add the item to the results array.
Return the results.
I'll leave any validation of the arguments (e.g., checking that test actually is a function) to you.

Understanding return function(x) from outer function(array)

I am learning JavaScript and got a bit confused with the following exercise. I did had to create a filter that would accept another functions as a sorting method. The thing that I cant understand a bit is how exactly the for loop passes the value to the x. Can you please explain?
function filter(arr, func) {
var result = [];
for (var i = 0; i < arr.length; i++) {
var value = arr[i];
if (func(value)) {
result.push(value);
}
}
return result
}
function inBetween(a, b) {
return function(x) {
return a <= x && x <= b;
}
}
function inArray(arr) {
return function(x) {
console.log(x)
return arr.indexOf(x) != -1;
}
}
var arr = [1, 2, 3, 4, 5, 6, 7];
alert(filter(arr, function(a) {
return a % 2 == 0
})); // 2,4,6
alert( filter(arr, inBetween(3, 6)) ); // 3,4,5,6
alert( filter(arr, inArray([1, 2, 10])) ); // 1,2
I'll take this line as example:
filter(arr, inArray([1, 2, 10])) );
inArray is called with arr = [1, 2, 10].
It returns the following (anonymous) function, for that particular arr:
function (x) {
return arr.indexOf(x) != -1;
}
So the original line now can be pictured as:
filter(arr, function (x) {
return [1, 2, 10].indexOf(x) != -1;
});
Now filter is called with func set to that anonymous function. The following code calls that function:
if (func(value)) {
result.push(value);
}
So when this func is called, this really means the above anonymous function is called, and the parameter x gets set to value at the moment of the call. It is just like with any other function call, the parameters of the function get the values of the arguments in the actual call.
Your filter function accepts the array to filter, and a function that does the filtering. In this part:
for (var i = 0; i < arr.length; i++) {
var value = arr[i];
if (func(value)) { //here, you invoke the function on the array's values
result.push(value);
}
}
So if, for instance, we look at the inBetween(3, 6) function, which is this:
return function(x) {
return 3 <= x && x <= 6;
}
(If you don't understand this part, read some more about Closures)
So now that function is simply a function that accepts a value and returns true/false on whether or not it's between 3 & 6.
You eventually get an invocation on each of the array's values (1, 2, 3 ... 7) with the above function.
Same goes for all other functions you listed.
In this line:
filter(arr, inBetween(3, 6))
You are not "passing a function", but passing the result from a function call. The result is another (anonymous/unnamed) function.
From this line:
function filter(arr, func) {
We know name the function passed as an argument to filter is named func, then:
if (func(value)) {
executes this function.
In JavaScript, a function is just an object like other objects. To differentiate "function object reference" from "function calls", you can look for ().
inBetween is a function object reference, inBetween(x, y) means calling this function.
Anonymous functions can be defined, assigned to a variable, and passed as an argument as well. It may be executed instantly after definition as well:
function() {
alert("called!");
}();
This may look exotic to people new to JavaScript though.

javascript callback function selection

So basically i need to use the each function that i implemented in the filter function so that the filter function return only item in myArray that is greater than 2. i m totally stuck.
var myArray = [1, 2, 3, 4, 5];
function each(collection, callback) {
for (var i = 0; i < collection.length; i++) {
callback(collection[i]);
}
}
function filter(collection, test) {
var returnAr = [];
for (var i = 0; i < collection.length; i++) {
if (test(collection[i])) {
returnAr.push(collection[i])
}
}
//instead of using "for(var i = 0; i < collection.length; i++)"
// how can i use the each function i implemented above?
return returnAr;
}
filter(myArray, function(n) {
return n > 2;
}
The each function calls its callback by passing each item in the array as a function argument. Therefore the correct way to call it would be:
each(collection, function(item) {
if (test(item)) {
// ...
}
}
Javascript doesn't quite provide a nice self-documenting way of specifying the function signature[1]. Normally this is solved by reading the API documentation. But if this is your own code and it doesn't come with API documentation then you need to look at how the callback is invoked. In this case it is called as:
callback(collection[i]);
Which tells us that the callback function should accept one argument which is a single item from the array.
[1]: also called prototype, no, not in the javascript sense of the word "prototype", in the C sense of the word "prototype"
You can't. The 'each' function above executes a callback on all items of a collection. But in 'filter' you are deciding whether you want an item in the resultant array based on result of the callback. So 'filter' and 'each' are both different operations. Whatever you are doing in the current function is right way of filtering.
The signature for each takes an array like thing, and a function that gets called with the element.
function each(collection, callback) {
for (var i = 0; i < collection.length; i++) {
callback(collection[i]);
}
}
It seems like your callback should just do what this does:
if (test(collection[i])) {
returnAr.push(collection[i])
}
Note that collection[i] is the first argument to the callback.
Your callback will have the form:
function(element) {
// do stuff
}
If you really only want to filter the array on the condition you give, this is all the code you need:
myArray.filter(function(item) { return item > 2 })

Categories

Resources