0

I was given this question by a prospective client to solve and I wasn't able to on time, only the makeSFns function can be altered and as I was altering to give specific square values to match the square function (based on position in arr) I kept getting the error of funcs[i] is not a function which is weird because the square function its being compared to is returning a number, how does something that's expecting a function match a number?

//Task: fix makeSFns function to show correct answer
var arr = [ Math.random(), Math.random(), Math.random(), Math.random() ];
var square = function (x) { return x * x; };

function makeSFns(arr, square) {
    var fns = [];
    for (var i = 0; i < arr.length; i++) {
        fns.push(function() {
            return square(arr[i]);
        });
    }
    return fns;
}

var funcs = makeSFns(arr, square);

isEqual = true;
for (var i = 0; i < arr.length; i++) {
    if (funcs[i]() !== square(arr[i])) {
        isEqual = false;
        console.log('wrong answer');
        break;
    }
}
if (isEqual) console.log('correct answer');

Some asking how I got the error, I tried finding a way to get position in arr and then returning

var position = 0; //outside the function

for (var i = 0; i < arr.length; i++) {
    return square(arr[i]);             //replacing the push function
    position + 1; 
 }
Olli
  • 512
  • 3
  • 6
  • 22
  • My first guess is that `funcs` ended up being empty, so `funcs[i]` was `undefined`, which is not a function. – 9000 Mar 12 '18 at 22:07
  • 2
    `I kept getting the error` ... run your snippet ... no such error ... the code you posted in the question is obviously **not** the code that produces the error ... as an aside ... `var i` -> `let i` – Jaromanda X Mar 12 '18 at 22:12
  • 1
    Show us *how* you altered the code so that it threw `funcs[i] is not a function`. – Bergi Mar 12 '18 at 22:13
  • @Olli is there any reason for passing _function(){return square(x);}_ in push? – yajiv Mar 12 '18 at 22:18
  • @JaromandaX, Bergi Updated, Yajiv that's how I got the question – Olli Mar 12 '18 at 22:22
  • oh, lol, you decided not to have any functions, and return after one iteration - no wonder funcs[i] isn't a function - you made `makeSFns` return the square of the first item ... i.e. `makeSFns` returns a single `Number` – Jaromanda X Mar 12 '18 at 22:24
  • wrong, that 2nd loop breaks after the first, and only checks if 1st is correct answer, but the 1st loop does contain functions. – George Mar 12 '18 at 22:25
  • wrong ... the `return square(arr[i])` is, as noted `//replacing the push function` - so not sure why you think the first loop would contain functions if he's got rid of replacing the `push` – Jaromanda X Mar 12 '18 at 22:30
  • @Olli - the challenge was to `fix makeSFns` - the fact that you tried adding something `outside the function` was your first mistake - your second mistake was a `return` inside a for loop. I'm intrigued that `a prospective client` would issue such a challenge. Most prospective clients know nothing about programming. Are you sure you didn't mean `a prospective employer` – Jaromanda X Mar 12 '18 at 22:34
  • Client/employer (a contract), I bid for contracts from time to time – Olli Mar 12 '18 at 22:46

4 Answers4

1

you are passing arr[i] into square function inside makeSFns but this value lost as it is not a true closure.

//Task: fix makeSFns function to show correct answer
var arr = [ Math.random(), Math.random(), Math.random(), Math.random() ];
var square = function (x) { return x * x; };

function makeSFns(arr, square) {
  return arr.map((num, i)=>()=>{
    return square(arr[i])
  });
}

var funcs = makeSFns(arr, square);

isEqual = true;
for (var i = 0; i < arr.length; i++) {
    if (funcs[i]() !== square(arr[i])) {
        isEqual = false;
        console.log('wrong answer');
        break;
    }
}
if (isEqual) console.log('correct answer');
George
  • 2,330
  • 3
  • 15
  • 36
  • it shows the inputs which are random numbers between 0 and 1, and then it shows the outputs, which are the squared version. 0.5*0.5 is 0.25 if you didn't know. – George Mar 12 '18 at 22:16
  • plus that's not the question, it doesn't matter it doesn't alert "correct answer" – George Mar 12 '18 at 22:17
  • but his question was why his function was not being called inside the second loop, and it was being called it was just that the value wasn't being passed to it. the comment is irrelevant, and he can easily put it in himself, since he wrote that code. unecessary – George Mar 12 '18 at 22:19
  • here you go, pedantic person – George Mar 12 '18 at 22:23
  • check the definition of pedantic – George Mar 12 '18 at 22:26
  • you do realise you've changed HOW the funcs are called ... from `funcs[i]()` to `funcs[i](arr[i])` ... i.e. you failed the test, because the challenge is to `fix makeSFns` ... not the surrounding code - thanks for playing though, and thanks for the attitude – Jaromanda X Mar 12 '18 at 22:27
  • hey you said something relevant ;) – George Mar 12 '18 at 22:34
  • I'm always relevant - just some people don't understand straight away – Jaromanda X Mar 12 '18 at 22:34
0

It is a classic, since the push function is called after the loop, it's referencing the last known i, it is not correctly captured.

//Task: fix makeSFns function to show correct answer
var arr = [ Math.random(), Math.random(), Math.random(), Math.random() ];
var square = function (x) { return x * x; };

function makeSFns(arr, square) {
    var fns = [];
    for (var i = 0; i < arr.length; i++) {
        (function(n) {
          fns.push(function() {
            return square(arr[n]);
          });
        })(i);
    }
    return fns;
}

var funcs = makeSFns(arr, square);

isEqual = true;
for (var i = 0; i < arr.length; i++) {
    if (funcs[i]() !== square(arr[i])) {
        isEqual = false;
        alert('wrong answer');
        break;
    }
}
if (isEqual) alert('correct answer');

More on the subject:

Edit: an interesting tranformation done by TypeScript when using the let keyword:

TS

for (let i = 0; i < arr.length; i++) {
    fns.push(function() {
        return square(arr[i]);
    });
}

JS

var _loop_1 = function (i) {
    fns.push(function () {
        return square(arr[i]);
    });
};
for (var i = 0; i < arr.length; i++) {
    _loop_1(i);
}
Cyril Gandon
  • 16,830
  • 14
  • 78
  • 122
0

It is because the array's reference is not available to the function being pushed into fns array, a classic javascript scenario.

//Task: fix makeSFns function to show correct answer
//Task: fix makeSFns function to show correct answer
var arr = [ Math.random(), Math.random(), Math.random(), Math.random() ];
var square = function (x) { return x * x; };

function makeSFns(arr, square) {
    var fns = [];
    for (var i = 0; i < arr.length; i++) {
        let myFn = function(myInput) {
            return square(myInput); 
        };
        fns.push(myFn.bind(null,arr[i]));
    }
    return fns;
}

var funcs = makeSFns(arr, square);

isEqual = true;
for (var i = 0; i < arr.length; i++) {
    if (funcs[i]() !== square(arr[i])) {
        isEqual = false;
        console.log('wrong answer');
        break;
    }
}
if (isEqual) console.log('correct answer');
Satya
  • 1,037
  • 3
  • 15
  • 34
0

There's a solution to this that requires changing one keyword only:

  function makeSFns(arr, square) {
    var fns = [];
-    for (var i = 0; i < arr.length; i++) {
+    for (let i = 0; i < arr.length; i++) {
       fns.push(function() {
         return square(arr[i]);
       });
     }
    return fns;
  }

Using let changes the scope of i and makes it behave like a separate closure. Each iteration in the for loop will have it's own instance of i.

Sebastián Grignoli
  • 32,444
  • 17
  • 71
  • 86