Divisible by 2?

The discussion starts here

This is only the beginning. As its name suggests, functional programming is centered around a theory of functions.

It would make sense to be able to pass them as arguments to other functions, and return a function from another function. Functions are considered first class objects in JavaScript, which means they can be used like any other object. They can be saved in variables, stored in an object, or passed as function arguments.

It will definitely be covered more. It’s a pretty big idea.

Ok in that example it seems the function is referring to the object no? btw was this statement true that I said?

No. If you were making a map, then the function would create a new array (which just so happens to be stored in new_s), and the contents of this new array would be given by callback(item[i]).

Instead, you are making a filter. In a filter, we create a new array (which just happens to be stored in the variable new_s) that only contains the items from the the original array where callback(item[i]) is true (or truthy).

I’m not sure what you mean. In JavaScript, functions are objects. Everything that is not a primitive data type (numbers, characters, booleans, etc) is actually an object. Arrays are actually objects with certain built in methods. Functions are actually objects. It’s all objects.

I must not be understanding what you are asking.

Ok I’m moving on for now. I found this which helps a bit and I think my issue with this problem is the syntax. If I can always use map and filter and I understand them, then I’ll just do that and see if I figure out how to use functions as arguments to other functions later. For me this just doesn’t click and I’m not strong on terms so when you’re explaining things some of it I’m not getting maybe

When you use map and filter, you are passing a function in as an argument…

In the example

const s = [23, 65, 98, 5];
const new_s = s.filter(number => number % 2 === 1);

the part

number => number % 2 === 1

is a function. It’s just one of the many ways to write a function.


What helped me a lot with understanding functional programming in the beginning was to look into the flow of the program, specifically - what gets executed when (and why).

I’ve added two console.logs to your code:

// The global variable
let s = [23, 65, 98, 5];

Array.prototype.myFilter = function (callback) {
    // Only change code below this line

    console.log('Array.prototype.myFilter was called');

    let newArray = [];
    for (let i = 0; i < this.length; i++) {
        if (callback(this[i])) {
            newArray.push(this[i]);
        }
    }
    // Only change code above this line
    return newArray;
};

var new_s = s.myFilter(function (item) {

    console.log('I am the callback that was passed into Array.prototype.myFilter');

    return item % 2 === 1;
});

console.log(new_s);

You can try to predict beforehand what gets logged (and when and how many times), or just try it out and then make sense of it afterwards, but I think it’ll give you a better idea of what’s happening.

1 Like

So

(function (item) {
    return item % 2 === 1;

becomes the callback because s.myFilter in used in variable new_s correct?

So if a new variable var hypothetical_s = s.myFilter(function (hypothetial) {..... was used it’d be inserted in callback position and it’d run the hypothetical argument or argument? (not sure if argument is the right word but hopefully you know what I mean)

No? Yes? Maybe? I’m not quite sure what level of understanding you’re at here, there’s something fundamental you’re missing, but I can’t quite figure out the exact thing (I think it’s important that we do, because you need that lightbulb moment)

A callback isn’t something special, it’s just a function. Functions can take any type of value as parameters, so a parameter can be a function as well. Nothing “becomes” the callback, filter is just a function that takes a function as an argument.

You can attach functions to objects. You can attach functions to an object’s prototype to share that function between all objects of that type.

An array is an object. myFilter is a function you’re attaching to the Array prototype so that you can use it on any array.

s is an array. Because you’ve attached the function myFilter to the Array prototype, you can use the function myFilter on s.

s and new_s are just the names of variables here if there’s any confusion on that front: they could be called dnriwfjrb and ccpdpgotnfje, not relevant. Values are just being assigned to variables to split the code up

1 Like

That line var new_s = STUFF is important. If you’d comment that all out, nothing would get logged. You would define a new method myFilter on the array prototype, but if no one uses it, it won’t be called.

JavaScript sees that you declare a variable new_s , and it tries to figure out what the value of that variable should be. So it looks at the right side of the =. It finds that you’re using a method myFilter on an array called s.

It sees that the Array.prototype.myFilter method exists, and that it’s a function, and that it takes a callback. So it applies that specific callback function you provided, and returns an array with items that pass your callback check function.

3 Likes

Yeah sorry I didn’t include it in my message but I understand it’s needed.

I like the way you explained it. So yes it sees that the Array.prototype.myFilter exists (this is what I was trying to get at with my last comment) which contains that loop function that puhes this[i] to newArray if the callback is true. The part where it takes a callback is the part I have difficulty with. The new function is inserted into the place of the callback right?

If I’m not using the right terms just ignore that part, first I want to conceptualize what’s happening. It also seems the earlier parts of js had great solution explanations with commented out explanations, but many of these ones don’t have anything freeCodeCamp Challenge Guide: Implement the filter Method on a Prototype

The callback function is a function argument just like any other argument to a function.

When you pass a number into a function as an argument, that number is used inside the function. When you pass an array into a function as an argument, that array is used inside the function. In the same way, when you pass a function into another function as an argument, that function is used inside the other function.

1 Like

Ok thank you. I guess it just gets confusing with multiple functions

It can be a bit weird to think of functions as entities that can be passed around like any other variable. This is what “functions are first class objects” really means. A lot of the stuff you can do with other variables/objects you can do with functions.

1 Like

So you have an array with some numbers, and you want take that array and produce a new one that only has the odd numbers in it.

Input: [23, 65, 98, 5]
Output: [23, 65, 5]


As discussed, can check whether a number is odd via n % 2 === 1.

I’m going to put that in a function, a. so that it’s in plain English, b. so I don’t have to keep typing it and c because it becomes important as I generalise the logic:

function isOdd(n) {
    return n % 2 === 1;
}

So, start with a loop:

let input = [23, 65, 98, 5];
let output = [];

for (let i = 0; i < input.length; i++) {
    if (isOdd(input[i])) {
        output.push(input[i]);
    }
}

console.log(output); // logs [23, 65, 5]

I take the input array, loop over it, and if the current value is odd, push it to the output array.


That is constrained to a single array, whereas I want it to be used for any array of numbers. So I can put the logic in a function so that it can take any array of numbers:

function filterOutEvens (numbers) {
    let oddNumbers = [];

    for (let i = 0; i < numbers.length; i++) {
        if (isOdd(numbers[i])) {
            oddNumbers.push(numbers[i]);
        }
    }

    return oddNumbers;
}

console.log(filterOutEvens([23, 65, 98, 5]));  // logs [23, 65, 5]

This is the same as the previous logic, but now I can call filterOutEvens, give it an array, and it will return a new array with only odd numbers.


That is constrained to just one operation: filtering out even numbers. What if I wanted to filter out odd numbers? I would have to write another function almost the same. Or numbers over 10. Or negative numbers. Or whatever. I’d need to write a new function every time.

isOdd is a function that takes a number and returns true if it’s odd and false if it’s even.

To generalise, it is a function that takes a single value and returns true or false depending on some condition you define.

So what I want is a function that takes an array + this generalised function (which could be isOdd) and apply it where isOdd is currently defined:

    for (let i = 0; i < numbers.length; i++) {
        if (isTrueFunction(numbers[i])) {
            oddNumbers.push(numbers[i]);
        }
    }

So what I want to do is this:

function filter (inputArr, isTrueFunction) {
    let outputArr = [];        ┃
                               ┗━━━━━━━━━━━━━━━━━┓
    for (let i = 0; i < inputArr.length; i++) {     ┃  
        if (isTrueFunction(inputArr[i])) {          ┃
                   ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
            outputArr.push(inputArr[i]);
        }
    }

    return outputArr;
}

I’ve changed all the names to be more generic, because now the function can filter anything, and the array can contain anything.

So to test it:

let oddNumbers = filter([23, 65, 98, 5], isOdd);
console.log(oddNumbers); // logs [23, 65, 5

Try with a different function:

function isEven (n) {
    return n % 2 === 0;
}

let evenNumbers = filter([23, 65, 98, 5], isEven);
console.log(evenNumbers); // logs [98]

The function doesn’t have to be defined seperately, can just pass it straight into the filter function, works exactly the same:

let numbersBelow50 = filter([23, 65, 98, 5], function below50 (n) {
    return n < 50;
});
console.log(numbersBelow50); // logs [23, 5]

There are different ways of writing functions, some that are much more terse and are useful for this as they save a load of typing. This acts exactly the same as the above call:

let numbersBelow50 = filter([23, 65, 98, 5], (n) => n < 50);
console.log(numbersBelow50); // logs [23, 5]

In the case of the challenge, you’re being asked to attach the function to Array.prototype. What this means is that it will be available to all arrays in your program. It is then used by chaining the function onto an array, so like [1,2,3].filter(someTrueFalseFunction) rather than filter([1,2,3], someTrueFalseFunction).

There is already a function called filter attached to Array.prototype, and I don’t want to override that, so I’ll call this [as per the challenge] myFilter.

I’m changing the clumsy name of isTrueFunction to callback, as that’s what a function in this position is commonly called (the main function runs and it “calls back” using the function that’s passed in as an argument).

Array.prototype.myFilter = function (callback) {
    let outputArr = [];

    for (let i = 0; i < this.length; i++) {
        if (callback(this[i])) {
            outputArr.push(this[i]);
        }
    }

    return outputArr;
}

NOTE I’m skipping over why I use this a bit – here, this refers to the array that myFilter is applied to, but I’m not going to get into the vagaries of it.

So to test again:

// Comparing two identical ways of defining this.
let oddNumbers;

// Passing in a externally-defined function:
function isOdd (n) {
    return n % 2 === 1;
}
oddNumbers = [23, 65, 98, 5].myFilter(isOdd);
console.log(oddNumbers); // logs [23, 65, 5]

// Same thing, but inline:
oddNumbers = [23, 65, 98, 5].myFilter((n) => n % 2 === 1);
console.log(oddNumbers); // logs [23, 65, 5]

// -----------------------------------------------------

// Again, comparing two identical ways of defining this.
let evenNumbers;

// Passing in a externally-defined function:
function isEven (n) {
    return n % 2 === 0;
}
evenNumbers =  [23, 65, 98, 5].myFilter(isEven);
console.log(evenNumbers); // logs [98]

// Same thing, but inline:
evenNumbers = [23, 65, 98, 5].myFilter((n) => n % 2 === 0);
console.log(evenNumbers); // logs [98]
2 Likes

Thank you for taking your time to write this out

Makes me glad I’m older than pocket calculators lol. 23÷2=11 remainder 1 can be written as a fraction where the remainder goes over the divisor yielding 11½ , then I started my digital life and 11½=11.5
(just for posterity’s sake I realize I’m too late for the discussion)

1 Like