Student trying to understand callback functions - javascript

Hi I'm trying to learn how to implement callback functions. My teacher has helped me out multiple times but I still can't pass data through the following equation below. I'm trying to get certain elements of array to get pushed into a new function if only they pass a test within the function. Please have a look and thank you for your input. An explanation as to why I get an empty array and resources to further my understanding would be appreciated.
// EACH DEFINITION
function each (collection, callback) {
for(var i = 0; i < collection.length; i ++){
callback(collection[i]);
}
}
// VARIABLE DECLARATION
var myArray = [1,2,3,4,5,6];
var isEven = function (num) {
return num % 2 === 0;
};
// IMPLEMENT DEFINITION
function implement(array, test){ // array = myArray, test = isEven
var arr = [];
each(array, function(item){
test(item);
});
if(test(array)){
arr.push(array);
}
return arr;
}
// IMPLEMENT INVOCATION
implement(myArray, isEven);

You are building arr outside the each() loop.
I would think your code would be like this:
// IMPLEMENT DEFINITION
function implement(array, test){ // array = myArray, test = isEven
var arr = [];
each(array, function(item){
if(test(item)) {
arr.push(item);
}
});
return arr;
}
Though in this case there is no reason for your implement() filtering function at all, since javascript Array prototype already has a filter method. You could simplify your call to this:
var filteredArray = myArray.filter(isEven);
Though you might also then want to change your isEven definition to be more correct as:
var isEven = function (num, index, array) {
In your case you don't need to work with the last two parameters.

// EACH DEFINITION
function each (collection, callback, results) {
for(var i = 0; i < collection.length; i ++){
callback(collection[i]);
}
console.log(results);
}
// VARIABLE DECLARATION
var myArray = [1,2,3,4,5,6];
var isEven = function (num, array) {
return num % 2 === 0;
};
// IMPLEMENT DEFINITION
function implement(array, test){ // array = myArray, test = isEven
var arr = [];
function filter (item) {
if (test(item)) {
arr.push(item);
}
}
each(array, filter, arr);
// If you return arr here, it will still be empty. You must pass it to functions it is being operated on.
}
// IMPLEMENT INVOCATION
implement(myArray, isEven);
Not only are you trying to push to arr outside of your loop, but you're trying to return arr before it has gained any values.

Two points:
First, your implementation of callback functions is correct. As far as the concept of callbacks goes, you are calling and passing the functions correctly.
However, your implement() function probably has a bug. You are not pushing to arr until after each() has already been called:
function implement(array, test) { // array = myArray, test = isEven
var arr = [];
each(array, function(item) {
result = test(item);
});
// This block should be in the loop itself
// It should also refer to item, not array
if (test(array)) {
arr.push(array);
}
return arr;
}
Try this fix based on the code you provided:
// EACH DEFINITION
function each(collection, callback) {
for (var i = 0; i < collection.length; i++) {
callback(collection[i]);
}
}
// VARIABLE DECLARATION
var myArray = [1, 2, 3, 4, 5, 6];
var isEven = function(num) {
return num % 2 === 0;
};
// IMPLEMENT DEFINITION
function implement(array, test) { // array = myArray, test = isEven
var arr = [];
each(array, function(item) {
if (test(item)) {
arr.push(item)
}
});
if (test(array)) {
arr.push(array);
}
return arr;
}
// IMPLEMENT INVOCATION
var result = implement(myArray, isEven);
console.log(result); // For snippet results

Your callback, as you defined it, is
function(item){
test(item);
}
this will only call test on each item and that's it. Since you want to take it further and add item to arr if test returns true, you should put that checking code inside the callback as well, making it
function(item){
if (test(item)) {
arr.push(item);
}
}
so that this function will be called for each of the item.
Also, this part
if(test(array)){
arr.push(array);
}
is incorrect because you are passing a whole array into isEven when isEven is expecting a number. test(array) will always return false; that's why your arr is empty.
Modifying your code to work as you wanted, it would be
// IMPLEMENT DEFINITION
function implement(array, test){ // array = myArray, test = isEven
var arr = [];
each(array, function(item){
if (test(item)) {
arr.push(item);
}
});
return arr;
}
Resources wise, there are callbacks tutorial widely available online, as well as best practices. You can easily find one that suits you best by googling.

It looks to me like the entire issue here is in the implementation section you denote. All of the other code looks adequate.
each(array, function(item){
test(item);
});
Alright, first let's examine this piece of code. You are making a call to your each function, which will use the callback anonymous function defined here as shown.
However, if you were to look at the each function itself, there is no return (which means it returns undefined by default). There is also no modification being done in each. As a result, this set of code has no effect on the execution of the code, and from certain advanced compilation technique may actually be removed by the V8 engine in chrome if that was being used.
This means the only aspect of your code which is executing is
var arr = [];
if(test(array)){
arr.push(array);
}
return arr;
At this point, test is still the isEven function, so you are basically asking this
if(array % 2 === 0) arr.push(array);
Arrays in JavaScript behave interestingly when used in conditional statements, and in this situation the array essentially has toString called on it (more in depth here: https://stackoverflow.com/a/10556035/1026459 , but basically when you have object === number then it will attempt to use toPrimitive on the object which results in a string), which makes it
if("1,2,3" % 2 === 0)
which is false. As a result arr is unchanged, and returned in its original state of [].

Related

Array that is consoled.log is not updating javascript

My array numbers are not updating when they are squared. I'm not familiar with callbacks and how to get their values to update the array. The foreach function runs the callback on each element of the array (updates the array passed in). forEach does not return anything.
//This function is to square a desired number
const square = a => (a*a)
//this function
function map(array, callback) {
const arrNew= [];
for (let i = 0; i < array.length; i++) {
arrNew.push(callback(array[i]));
}
return arrNew;
}
function forEach(array, callback) {
let newArray = [];
for (let i = 0; i < array.length; i+=1) {
callback(array[i]);
newArray.push(callback(array[i]));
}
}
var myarray = [10,20];
forEach(myarray, square);
// this should log 100,400 but is instead doing 10,20
console.log(myarray);
You need to return the newArray in forEach
//This function is to square a desired number
const square = a => (a*a)
function forEach(array, callback) {
let newArray = [];
for (let i = 0; i < array.length; i+=1) {
newArray.push(callback(array[i]));
}
return newArray
}
var myarray = [10,20];
myarray = forEach(myarray, square);
// this should log 100,400 but is instead doing 10,20
console.log(myarray);
For this example you can use the below code.
const myArray = [10, 20];
const squaredArray = myArray.map((item) => item * item);
console.log(squaredArray);
You can use map when do you want access de all items in your array and return one value. The "forEach" you access also your array, but the "forEach" can't return any value.
I think you might be over complicating this process. To get your desired result you can simply use the map function. For example:
var myarray = [10,20]
console.log(myarray.map((number) => number * number))
If you run this code you will get [100,400] logged in the console.
What is happening here is that we are calling the map method on the array. This method is taking a callback function. The callback function is this part
(number) => number * number)
So what is happening here? The map method is iterating over the provided array, and for each element, it is running that function, then returns the results in a new array.
I would suggest watching some videos and reading more about higher-order functions (which .map is one example of)
This video helped me alot when I was first learning about them
.

why isn't recursion working? i am trying to display n to 1 series

Javascript Recursion
why isn't recursion working here? i see coundown(n-1); isn't working for some reason. I want to display [5,4,3,2,1]
function countdown(n){
if (n<1){
return [];
} else {
var myArr = [];
myArr.push(n);
countdown(n-1);
return myArr;
}
}
countdown(5);
Your code creates a new array at every recursive call, puts one value in it and returns it. Nothing is done with the array that is returned, as each execution instance of your function seems only interested in its own array, which it returns.
You need to create one array, and extend that while backtracking out of recursion, each time making sure you capture the array that the recursive call gives you back as return value:
function countdown(n) {
if (n < 1) {
// This is the only time you should create an array:
return [];
} else {
// Get the array that comes out of recursion!
let myArr = countdown(n-1);
// Prefix the current value into it
myArr.unshift(n);
// And pass that extended array further up
// the recursion tree:
return myArr;
}
}
console.log(countdown(5));
Written a bit more concise it could become:
const countdown = (n) => n < 1 ? [] : [n].concat(countdown(n-1));
console.log(countdown(5));
And without recursion:
const countdown = (n) => Array.from({length: n}, (_, i) => n - i);
console.log(countdown(5));
var myArr =[];
function clearandcountdown(n)
{
myArr = []; // clearing the array each time when the function called
return countdown(n); // calling the recursion function
}
function countdown(n){
if (n<1)
{
return []
}
else
{
myArr.push(n);
countdown(n-1);
}
return myArr;
}
document.write(clearandcountdown(5));
You must use this code here you created myArr inside the else statement and you are setting it to empty for every function call here I used one extra function to clear the array each time when you call the function. Hope this will clear your doubts. Thank You :)

How do I update a variable inputted to a function?

I am trying to make a function in my js file that will remove an item from an array and then save the new array to the variable. But here's the thing: I don't want it to only save ONE variable, I want it to save any array variable that I input. What I mean is something like this:
const list = [1,2,3];
function removeItem(array,index)
{
let newArray = [];
for(var i = 0 ; i < array.length ; i++)
{
if(i != index)
{
newArray.push(array[i]);
}
}
array = newArray; // where it saves the variable
}
removeItem(list,0);
You can create a prototype function, see this
Array.prototype.removeItem = function(what) {
if (this.indexOf(what) >= 0) this.splice(this.indexOf(what), 1);
}
var foo = [1, 2, 3];
foo.removeItem(2);
console.log(foo);
You just want arr.splice(i, 1);
The simplest way is probably to return the new array and let the calling code decide where to save it:
function removeItem(array, index) {
let newArray = [];
for(var i = 0 ; i < array.length ; i++) {
if(i != index) {
newArray.push(array[i]);
}
}
return newArray;
}
And:
list = removeItem(list, 0);
This is probably ideal because modifying objects passed to a function as arguments is generally unexpected. It's more common to return the new value.
If you want to modify the array in place then you'd need to only make edits to the array reference directly in the function and not use any kind of newArray copy. Which could be as simple as:
function removeItem(array, index) {
array.splice(index, 1);
}
And:
removeItem(list, 0);
But this could be very unintuitive since list is an argument to the function and you're not calling anything on list itself. Contrast that to the syntax used in splice which is called on the array itself and implies that it may modify that array:
list.splice(0, 1);
(Though some array methods in JavaScript return a new array while others modify the array in place. It's always best to refer to the documentation to make sure.)

Javascript how do i get my function array to give me more then the first value

function countUniqueItems(arr) {
nums = [];
for (i = 0; i < arguments.length; i++) {
const item = arr[i];
console.log(i);
//console.log(item);
if (nums.includes(arr) === true) {
//console.log('8 is in the array');
//nums.push(arr)
} else {
nums.push(arr);
//console.log('8 is NOT in the array');
//nums.push(item)
}
}
return nums;
}
countUniqueItems(1, 2);
So it will give back the first argument which is 1 but i want it to be able to say argument 2 and 3 and so on
So you need to pass an array into the function, in place of 1,2 pass [1,2].
Then inside your function, you should use arr.length in place of arguments.length.
Then you look at your logic for the loop, you are pushing atm arr into nums, but if you pass and array that isn't really want you want, you should be pushing item as that is the variable which represents your current element from the array.
It looks from you comments like you're trying to make a unique list of inputs. Perhaps something like this would do the trick.
EDIT: Updated to use arguments
function uniqueNumbers() {
let arrayOfArguments = [...arguments]
let uniqueNums = [];
arrayOfArguments.map(i => !uniqueNums.includes(i) ? uniqueNums.push(i) : null);
return uniqueNums;
};
console.log(uniqueNumbers(1,2,3,3));
you should either pass an array to countUniqueItems or use the arguments keyword in the for-loop.
Your code is only seeing 1 (as arr inside the function).
basic implementation to find unique items
function countUniqueItems(...arr) {
let nums = [];
for (let num of arr) {
if (nums.indexOf(num) === -1) nums.push(num);
}
return nums;
}
console.log(countUniqueItems(1, 2, 1));
Using Set you can remove the duplicate values, you dont need to do logic run the loop to find the unique values from array.
const a = [1,2,3,3];
const uniqueSet = new Set(a);
uniqueSet.size
let uniqueFinder = arr => { const uniqueSet = new Set(arr); return uniqueSet.size}
const arrywithduplicates = [1,2,3,3,4,4];
uniqueFinder(arrywithduplicates) // return 4
Read more about Set : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

return function returns array values using Closure

I'm trying to use Closure in JS in order to declare a function named **expandArray()** which contain an Array named **myArray**and Returns an anonymous function that directly modifies myArray by increase the values by 1 than the returned function then returns the value of **myArray**. My Problem here one the last part where the returned function return a function not Array value ?!
This is my code
function expandArray() {
const myArray = [1, 1, 1];
return function () {
myArray.forEach( function (num, index, myArray) {
myArray[index] = num + 1;
});
return myArray;
};
}
console.log(expandArray());
As its closure, you have invoked it only once like expandArray() , which return the function itself, which is below
ƒ () {
myArray.map( function (num, index, myArray) {
myArray[index] = num + 1;
});
return myArray;
}
you need to invoke it again to get your result back as below
expandArray()() //[2, 2, 2]
Ref: How do JavaScript closures work?
You've written a function that returns a function when you run it:
function expandArray() {
const myArray = [...];
// return this when we run expandArray():
return function() {
...
}
}
So if you run expandArray(), it is going to return your anonymous function. Exactly as you wrote it to do.
If you then want to get an actual reference to that internal myArray, you'll now need to actually run that returned function, so:
var getMyArray = expandArray();
var result = getMyArray();
console.log(result);
Just fyi, you are doing something very similar to the memoization pattern.
To address your problem: as everyone else has already said, you return a function from expandArray. What you want is to return the closed array (after incrementing every element).
To do this, you can use something called immediately-invoked function expression in combination with arrow functions to simplify your code:
const expandArray = (() => {
const myArray = [1, 1, 1];
return () => {
myArray.forEach((num, index) => {
myArray[index] = num + 1;
});
return myArray;
};
})();
console.log(expandArray());
console.log(expandArray());
console.log(expandArray());
There are a couple of things incorrect with your code.
you can't change the values of variables declared within const. In the case of Objects and Arrays, you aren't allowed to assign a new reference with a new Array or Object. We change the declarative operator to let instead of const.
myArray.map doesn't mutate myArray, it returns a new array based on the input from myArray and your passed in function that adds 1 to each value. We can fix this by assigning myArray.map to the already declared myArray. That is to say, we're overwriting the old Array with a new one. This is why const in the above bullet point won't work.
Your map function parameters are unnecessary The parameters for it that are most often used are the first two available, which is the item in the array and the index of that item. Since we're iterating over each number using map the function can simply return the item (declared as num in your code) plus 1. This will return a new array with your changed values. So we don't need the index at all..
When you return a function from a function you need to invoke both to get the second return value. When using a closure you need to keep a reference to the initial returned function. This is confusing but if you think of it as levels - in your expandArray function you have two levels. The function itself and the function you're returning. when you call expandArray() you're invoking the first level, and making the second level available to be invoked, this is why expandArray() returns the second function and expandArray()() will return the value from the second function. We save the returned function in a variable called add_to_array by setting it equal to expandArray(), and then we consistently invoke add_to_array to return the new Array with the changed values.
This is the most confusing part of closures, but what is happening is that the add_to_array variable is like a wedge in the function. It stops myArray from being deleted by the Browser because it requires the function to exist in the event that it needs to be invoked again. It's kind of like a bookmark in a book. For the story to make sense whenever you open it, you don't just tear out the pages before the bookmark. You keep the story intact because in five years when you come back to it you may need to read the previous pages at the bookmark to remember where you were. A closure works the same way. It can't remove the variables from the initial expandArray function call because add_to_array is a placeholder in the middle of it. The reference point keeps the book open.
(for more info on closures you can check out this article here Destroying Buildings - A Guide to JavaScript Closures)
function expandArray() {
let myArray = [1, 1, 1];
return function () {
myArray = myArray.map( function (num) {
return num + 1;
});
return myArray;
};
}
let add_to_array = expandArray();
console.log( add_to_array(),add_to_array(),add_to_array() );
In your original code, you're only getting the return value of expandArray(), which is the function you're trying to use as a closure. In order to get the closure's return value, try this out:
function expandArray() {
const myArray = [1, 1, 1];
return function () {
myArray.forEach( function (num, index, myArray) {
myArray[index] = num + 1;
});
return myArray;
};
}
console.log(expandArray()());
The second set of parentheses after the call to expandArray() will invoke the closure and return the values you're seeking.
Old post, but I still like to contribute. I came up with this solution, as I think you want add something to the array instead of incrementing the numbers.
function expandArray() {
let myArray = [1, 1, 1];
return function() {
myArray.push(1)
return myArray
}
}
const array = expandArray();
const result = array();
console.log(result)
3years after this question was posted this lines of code works fine for me
function expandArray() {
let myArray = [1, 1, 1];
return function() {
myArray.push(1);
return myArray;
};
}
let newArray = expandArray();
console.log(newArray());
Note this is my first contribution on stack overflow.

Categories