Function invocation every secoтd time? - javascript

Recently I got test task at the project, it is fairly simple:
const _ = require('underscore');
// Challenge:
//
// Write a function that accepts as argument a "function of one variable", and returns a new
// function. The returned function should invoke the original parameter function on every "odd"
// invocation, returning undefined on even invocations.
//
// Test case:
// function to wrap via alternate(): doubleIt, which takes a number and returns twice the input.
// input to returned function: 1,2,3,4,9,9,9,10,10,10
// expected output: 2, undefined, 6, undefined, 18, undefined, 18, undefined, 20, undefined
const input = [1,2,3,4,9,9,9,10,10,10];
const doubleIt = x => x * 2;
const alternate = (fn) => {
// Implement me!
//
// The returned function should only invoke fn on every
// other invocation, returning undefined the other times.
}
var wrapped = alternate(doubleIt)
_.forEach(input, (x) => console.log(wrapped(x)))
// expected output: 2, undefined, 6, undefined, 18, undefined, 18, undefined, 20, undefined
And my solution was:
const alternate = (fn) => {
let odd = false;
return (x) => {
odd = !odd;
if (odd) {
return fn(x);
}
return undefined;
};
};
// An alternate solution if ternary operator (?) is allowed according to coding standards used on the project.
// Sometimes it's treated as bad practise.
const alternateShort = (fn) => {
let odd = false;
return (x) => (odd = !odd) ? fn(x) : undefined;
};
And I got the reply that tech lead didn't like my solution at all and I'm not hired to the project.
I'm really confused, do you have any idea what else he could expect?

Related

Calling a function next to another function javascript explanation

Can someone please explain to me this bit of code
return pipe(...fns)(this);
I understand if we didn't have (this), so we returned the reduced functions but (this) confused me.
Full source code here. line 72
https://stackblitz.com/edit/typescript-hv4ntb
It is the same as this:
const pipeFunction = pipe(...fns); // pipe returns a function
const result = pipeFunction(this); // call the returned function with this as argument
return result; // return the result
So if you ever see something like variable(...)(...) you should assume that the variable evaluates to a function that returns a function, like this perhaps:
const variable = (a) => (b) => a + b;
variable(4)(2);
// ==> 6
const partial = variable(8)
[1, 2, 3].map(partial);
// ==> [9, 10, 11]
Javascript is said to have first-class functions (you can read more here on MDN), meaning that you can do with functions what you can do with other types. Storing them into a variable, passing them as argument to other functions, as well as using them as return value from other functions.
The thinking is, just think of functions as values.
In your use-case, a function is just being returned from another function:
function multiply(times) {
return function(number) {
return number * times;
};
}
// as you can see, we are storing the "returned function"
// resulted from calling `multiply` into the `double` variable.
const double = multiply(2);
console.log(
double(5)
);
Now, of course, you could short-circuit the double variable,
and just call the returned function straight away.
function multiply(times) {
return function(number) {
return number * times;
};
}
console.log(
multiply(2)(5)
);

I keep getting "undefined" as an error, function not returning anything

I've been trying to figure out why I keep getting undefined, and I don't really see why, I know that I would get undefined if my function isn't returning anything, but in this case, even with returning in two places, I am still getting undefined. I think that the reason for that is that maybe I am not returning the value from innerFunc properly? I'm thinking maybe there's something wrong with my syntax here: innerFunc(arg) but I'm not sure how it is wrong, or what to change it to.
Any help would be appreciated.
Instructions:
Create a function "fastCache" that takes one argument (a function) and returns a function. When fastCache is invoked it creates an object that tracks calls to the returned function, where each input to the returned function is associated with its output. Every subsequent call to that returned function with the same argument will return the output directly from the object, instead of invoking the original function again.
function fastCache(func) {
const obj = {};
function innerFunc(arg) {
for (const key in obj) {
if (key === arg) {
return obj[arg]
} else {
obj[arg] = innerFunc(arg)
return innerFunc(arg)
console.log(obj[arg])
console.log(arg)
console.log(innerFunc(arg))
}
}
}
return innerFunc
// console.log(innerFunc(arg))
}
//example:
//SINGLE ARGUMENT CASE
const multiplyBy2 = num => num * 2;
const cachedMultiplyBy2 = fastCache(multiplyBy2);
If you want a simple cache function:
function cacheWrapper(fn) {
let cache = { };
return arg => {
if (cache.hasOwnProperty(arg)) {
return cache[arg];
}
return cache[arg] = fn(arg);
};
}
function test(x) {
return x * 2;
}
let testCached = cacheWrapper(test);
console.log(testCached(2));
console.log(testCached(2));
The key here is hasOwnProperty which will tell you if that cache slot is occupied or not. This avoids having to fumble around and find out the hard way with iteration.
If the number isn't already in the object, it won't be found in the loop.
function fastCache(func) {
const obj = {};
function innerFunc(arg) {
for (const key in obj) {
if (key === arg) {
return obj[arg]
}
}
obj[arg] = func(arg)
return func(arg)
}
return innerFunc
// console.log(innerFunc(arg))
}
//example:
//SINGLE ARGUMENT CASE
const multiplyBy2 = num => num * 2;
const cachedMultiplyBy2 = fastCache(multiplyBy2);
const result = cachedMultiplyBy2(8);

Writing _.Memoize from underbar in Javascript

I am currently studying Javascript and am doing the underbar project(re-writing the _underbar library).
I have solved all of them but one, as I am stuck on _.memoize
This is my current code
_.memoize = function(func) {
var cache = {};
var slice = Array.prototype.slice;
return function() {
var args = slice.call(arguments);
if (args in cache) {
return cache[args];
} else {
return (cache[args] = func.apply(this, args));
}
}
};
This is the test case I am failing to pass
// Here, we wrap a dummy function in a spy. A spy is a wrapper function (much like _.memoize
// or _.once) that keeps track of interesting information about the function it's spying on;
// e.g. whether or not the function has been called.
it('should run the memoized function twice when given an array and then given a list of arguments', function() {
// Be careful how you are checking if a set of arguments has been passed in already
var spy = sinon.spy(function() { return 'Dummy output'; });
var memoSpy = _.memoize(spy);
memoSpy([1, 2, 3]);
expect(spy).to.have.been.calledOnce;
memoSpy(1, 2, 3);
expect(spy).to.have.been.calledTwice;
});
Or in words ' It should run the memoized function twice when given an array and then given a list of arguments'
I tried to change the args by checking if the first was a array and if not making all the arguments an array, but to no luck. I also tried to re write the code to hold a result and use Fibonacci to store the cache but it would run the function twice.
How would I resolve this test case?
Thanks
Using cache[args] doesn't make much sense because args is an array, and using bracket notation will convert it to a string. You could make an array of arguments arrays to their return values, then compare the arrays:
const _ = {};
_.memoize = function(func) {
const allArgs = [];
return function(...args) {
// Try to find matching arguments in allArgs
const result = allArgs.find(
item => item.args.length === args.length && item.args.every(
(arg, i) => args[i] === arg
)
);
// If a matching argument array was found, return the result:
if (result) return result.returnValue;
const returnValue = func.apply(this, args);
allArgs.push({ args, returnValue });
return returnValue;
}
};
const memoizedSum = _.memoize((...args) => {
console.log('called with', args);
return args.reduce((a, b) => a + b, 0);
});
console.log(memoizedSum(1, 2, 3));
console.log(memoizedSum(1, 2, 3));
console.log(memoizedSum(4, 5, 6));
Another option with less computational complexity would be to create a Map for each argument. For example, a call with arguments of 1, 2, 3 could result in a data structure of:
Map([[
1, { nested: Map([[
2, { nested: Map([[
3, { result: 6 }
]])}
]])}
]])
Then you'd find whether a nested result exists for the given arguments by recursively iterating through the Maps with .get for each argument, instead of iterating through all arguments the function has been called with so far. The code would be complicated, but it'd be more efficient.
Your function looks good, just one little detail. In your last return you should cache it first and then return it:
cache[args] = func.apply(this, args);
return cache[args];

NodeJS output is function instead of printed strings

I am having trouble printing the correct result in NodeJS, why isn't my code printing the strings in the correct way ? I feel like console.log is not called at all. Why do I get :
[Function]
[Function]
[Function]
[Function]
Expected result:
Tigrou (buddy of Spider) was here!
Spider (buddy of Tigrou) was also here!
Tigrou (buddy of Spider) are in a boat...
1 (buddy of 2)3
The code I thought would work:
function build_sentence(...args)
{
var i = 0
var s = ""
for (let arg of args)
{
if (i == 1)
{
i++
s += "(buddy of " + arg + ") "
}
else
{
s += arg + " "
i++
}
}
return s
}
function mycurry(build_sentence)
{
return function(...args)
{
if (!args)
{
return build_sentence();
}
else
{
return mycurry(build_sentence.bind(this, ...args))
}
}
}
const curried = mycurry(build_sentence);
console.log(curried("Tigrou")("Spider")(" was here!"))
console.log(curried("Spider")("Tigrou")(" was also here!"))
console.log(curried("Tigrou", "Spider", " are in a boat... "))
console.log(curried(1)(2, 3))
Here's a full working solution for you (except the string spacing)
const build_sentence_n_params = 3;
function build_sentence(...args)
{
var i = 0
var s = ""
for (let arg of args)
{
if (i == 1)
{
i++
s += "(buddy of " + arg + ") "
}
else
{
s += arg + " "
i++
}
}
return s
}
// A generic n-parameter curry helper, that returns fn return value
// after n-parameters given, and continues currying with more parameters.
// Note that in this configuration, the caller would also need
// to know how many parameters to give before a value is returned.
function ncurry(fn, n)
{
// Note that we have to return an outer function here, rather than just
// returning `curry` function directly, so `params` is unique for each
// initial call to `curried`.
return function(...args) {
// Initial arguments (note: we can use this array without copy, since
// rest params will always be a new array)
const params = args;
// This function contains the return logic without having to duplicate it below
function next() {
// If we have all params, call the given function, otherwise
// return the curry function again for more parameters
return !n || params.length >= n ? fn.call(null, ...params) : curry;
}
// The outer function is only called once, but this curry
// function will be called for each additional time.
function curry(...args) {
// Accumulate additional arguments
params.push(...args)
return next();
};
return next();
};
}
function mycurry(build_sentence)
{
// Call the generic n-parameter curry helper to generate
// a specific curry function for our purposes.
return ncurry(build_sentence, build_sentence_n_params);
}
const curried = mycurry(build_sentence);
console.log(curried("Tigrou")("Spider")(" was here!"))
console.log(curried("Spider")("Tigrou")(" was also here!"))
console.log(curried("Tigrou", "Spider", " are in a boat... "))
console.log(curried(1)(2, 3))
Your method invocations are quite confusing so I took the liberty to simplify your code.
If you run the following:
const curried = build_sentence("Tigrou", "Spider", " was here!");
console.log(curried);
You will get your desired output:
Tigrou (buddy of Spider) was here!
Why you are using the mycurry method is beyond my understanding.
When debugging your code, the sentence is already built on the first invocation, and what happens is that the subsequent invocations are not really invocations of the inner functions.
the main issue about our code is that !args is all the time false, as [] to boolean is also true.
you have to use !args.length. however you should add an extra call to your curried function usage like curried(1)(2, 3)().
the other approach is using comparison of curried function number of required params (build_sentence.length) and the number of params passed (args.length), but it's not working with spread scenario.
upd:
ex.1 - using Function.prototype.length property
const curry = fn =>
(...args) =>
fn.length <= args.length
? fn(...args)
: curry(fn.bind(this, ...args));
const func1 = (a, b, c) => `${a},${b},${c}`;
const func2 = (...args) => args.join(',');
console.log(curry(func1)(1,3)(4));
console.log(curry(func1)(1,3,4));
console.log(curry(func1)(1)(3)(4));
console.log(curry(func2)(1));
in this case currying will work fine for func1 (function that has enumerable number of arguments) because func1.length = 3. however for func2 - func2.length = 0, so (curry(func1)) will be executed after first call.
ex.2 - using number of arguments passed
const curry = fn =>
(...args) =>
!args.length
? fn()
: curry(fn.bind(this, ...args));
const func = (...args) => args.join(',');
console.log(curry(func)(1)(2,3,4)(5)());
in this case function currying will only return result of fn executing, when called with no arguments. however it will handle innumerable arguments of curried function properly.

taking argument in to another function

one argument——another function——and returns a "memoized" version of that function. A "memoized" version of a function caches and returns the results of its call so that when it is called again with the same input, it doesn’t run its computation but instead returns the results from cache. Note that previous results should be retrievable in any order without re-computation.
foo = function (x) {
console.log("calculating!");
return x + 5;
}
var memoizedFoo = memoize(foo);
memoizedFoo(5);
// calculating!
// 10
memoizedFoo(5);
// 10 (notice how 'calculating!' is not printed this time)
memoizedFoo(10);
// calculating!
// 15
I assume the question is how to write memoize. You can store the first result for a given argument in a container, and return a function that will use that container if it can.
Here's an ES5 example, which only works for argument values that can be usefully converted to strings, like strings, numbers, booleans:
function memoize(f) {
// Storage for this function's results
var values = Object.create(null);
return function(arg) {
// Already have it?
if (Object.hasOwnProperty.call(values, arg)) {
// Yes, return it
return values[arg];
}
// No, get it, remember it, and return it
return values[arg] = f(arg);
};
}
var foo = function (x) {
console.log("calculating!");
return x + 5;
};
var memoizedFoo = memoize(foo);
console.log(memoizedFoo(5));
// calculating!
// 10
console.log(memoizedFoo(5));
// 10
console.log(memoizedFoo(10));
// calculating!
// 15
If you need to support other kinds of arguments, you need to use another structure, or a Map polyfill. Which brings us to...
...in ES2015+, we can use Map, which makes it work with a broader range of argument values:
function memoize(f) {
// Storage for this function's results
const values = new Map();
return function(arg) {
// Already have it?
if (values.has(arg)) {
// Yes, return it
return values.get(arg);
}
// No, get it, remember it, and return it
const value = f(arg);
values.set(arg, value);
return value;
};
}
const foo = function (x) {
console.log("calculating!");
return x.foo + 5;
};
const memoizedFoo = memoize(foo);
const firstArg = {foo:5};
console.log(memoizedFoo(firstArg));
// calculating!
// 10
console.log(memoizedFoo(firstArg));
// 10
const secondArg = {foo:10};
console.log(memoizedFoo(secondArg));
// calculating!
// 15
// Note that maps consider equivalent but distinct objects as different (of course),
// so we don't get the memoized reslt from
// `firstArg` here
const thirdArg = {foo:5};
console.log(memoizedFoo(secondArg));
// calculating!
// 10

Categories