How does this algorithm work? What does this code mean? - javascript

I am trying to figure out what the code below means. I got it from freecodecamp.com and I am doing one of the challenges on there. The challenge is called "Seek and Destroy". I am just one of those people that has to understand what is going on in order for me to proceed, so can someone please explain to me how this code works and what is does from top to bottom?
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
return arr.filter(function(element){
return args.indexOf(element) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
For example, what is the "element" parameter in the function for .filter() method? How does javascript know what "element" is in the first place? I have been asking people and reading descriptions of solutions, but every solution I read is very vague and is still confusing to me.
Side note: I already know the algorithm removes values from arr, but I just want to know how it does it.

var args = Array.prototype.slice.call(arguments);
This line takes all the arguments given in the function and places them into an array. In this case the value is [[1, 2, 3, 1, 2, 3], 2, 3].
return arr.filter(function(element){
...
});
The .filter() function takes an anonymous function which can have three parameters: element, index, and array. In this case the function iterates through the array arr being [1, 2, 3, 1, 2, 3]. For this function if true is returned the element is kept, if false is return the element is removed from the array.
return args.indexOf(element) === -1
Here we check if the array args has the element element. The .indexOf() method returns the index that the element is found, if it's not found -1 is returned. What this means is if the element is not found it returns true, otherwise it will return false (removing it from the array) if it was found.
Basically the above statements means for each element in the array [1, 2, 3, 1, 2, 3] if it's found in [2, 3] then it's removed. It technically checks [[1, 2, 3, 1, 2, 3], 2, 3], but the first check is against an array compared to an element which will always be false so it might as well be omitted.
Below are some brief comments stating what I mentioned above.
function destroyer(arr) {
// An array of the arguments
var args = Array.prototype.slice.call(arguments);
// For every element in the array arr remove elements found in args
return arr.filter(function(element){
return args.indexOf(element) === -1;
});
}

Related

Javascript recursion related question about push() and unshift() methods working opposite

function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
return countArray;
}
}
console.log(countup(5));
After running the above code it returns an array: [1, 2, 3, 4, 5], but push() adds new values at the end of an array so when value of n was 5 it should push 5 to the end of the array and when value of n got 4 it should push 4 at the end of the array like [5,4].
So why not it is returning [5,4,3,2,1] ? It is hard to understand what is happening in this code probably because of this recursion. Isn’t unshift() (which add new values to start of the array) should return [1,2,3,4,5] and push() [5,4,3,2,1] why opposite is happening?
As #Joseph stated in a comment the second to last function call would get pushed to the array first, then it would return that array, where it immediately adds the next number up the call stack.
Here are the steps being taken.
Initial call enters itself recursively n times, where the bottom call returns [] and then [1], [1, 2] ... [1, 2, ..., n] all the way up the call stack where upon the first function call ends and the program does something else.
To get [n, ..., 2, 1] you need to use the Array.prototype.unshift() method, which takes any javascript primitive type, ie String, Number, Boolean, and Symbols, in a comma separated format, like countArray.unshift(4, 5) or countArray.unshift(...anotherArray), and adds them to the beginning of the array.
ie
let someArr = [3, 2, 1];
someArr.unshift(5, 4);
console.log(JSON.stringify(someArr));
// outputs [5, 4, 3, 2, 1]
or
let someArr = [1, 2, 3];
let anotherArr = [5, 4]
someArr.unshift(...anotherArr);
console.log(someArr);
// outputs [5, 4, 1, 2, 3]
Where the output of
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.unshift(n);
return countArray;
}
}
console.log(countup(5));
will be [5, 4, 3, 2, 1] tested with node in Vscode.
One useful way to think about this is to start by imagining you already had a function that does what you want for lower values, and then see how you would write one that works for higher values. That imaginary function should be a black box. All we should know is that it does what we want in the case of lower values. We don't care about its implementation details.
So lets say we had a function imaginaryBlackBox, and we knew that it returned the correct countUp values that we want. So, for instance, we know that imaginaryBlackBox (4) returns [1, 2, 3, 4].
Now, knowing that, how might we write a function that works for an input of 5 as well? How about something like this:
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = imaginaryBlackBox(n - 1);
countArray.push(n);
return countArray;
}
}
Again, we don't know how imaginaryBlackBox works. We just know that it returns the correct result for lower values of n. Our base case remains obvious. For another case, some n greater than 0, we call imaginaryBlackBox(n - 1), and by our basic assumption, we know that will return [1, 2, 3, ..., (n - 1)], which we store in countArray. Then we push n onto that array, to end up with [1, 2, 3, ..., (n - 1), n]. We return that value and we're done.
Now here's the trick. We know an implementation of imaginaryBlackBox -- it's the function we're writing! So we can simply replace it with countUp and know it will work.
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countUp(n - 1);
countArray.push(n);
return countArray;
}
}
This required a few assumptions, and they are important to all recursive functions:
There is at least one base case whose value we can calculate without any recursive call. Here, when n is < 1, we simply return [].
For other cases, we can break down our problem into one or more recursive cases, where the input is in some clear and measurable way closer to a base case, so that subsequent steps will hit a base case in a finite number of calls. Here we reduce n by 1 on every step, so eventually it will be below 1.
Our function is effectively pure: its outputs depend only on its inputs. That means we can't count on changing a global variable or reading from one that might be changed elsewhere. Note that I use the qualifier "effectively" here; it doesn't matter if this function has observable side-effects such as logging to the console, so long as its output is dependent only on its input.
Anytime you have those conditions, you have the makings of a good recursive function.

How to navigate and pull from an object in JavaScript

I am being given the function call destroyer([1, 2, 3, 1, 2, 3], 2, 3);. I want to be able to pull from the last 2, 3 part after the initial object, but I do not know how to go about this.
return arr[6]; and return arr[1][0] both return nothing. I am expecting to see 2 or 2, 3 (Last two numbers)
I tried researching Property Accessors, but I think I was looking in the wrong place for my answer.
Here's my full code:
function destroyer(arr) {
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
Instead of getting the first array [1,2,3,1,2,3]
I want to get the parts after the array:
[1,2,3,1,2,3],2,3
You're destroyer function is only taking one argument, but you're passing it 3.
You have two options:
Use arguments to get an array-like of all the passed arguments, you'll then need to combine that with a method like slice to get only the second and third arguments. Additionaly since arguments isn't technically an array, you need to convert it to one before you can call a method like slice. My example uses Array.from however that is only available on newer browsers.
function destroyer(arr) {
return Array.from(arguments).slice(1,3);
}
console.log('Result: ', destroyer([1, 2, 3, 1, 2, 3],2,3));
Add additional parameters to your function definition. This is probably easier if you know you'll have exactly 3 arguments. And there are far fewer gotchas compared to using the magic arguments variable.
function destroyer(a, b, c) {
return [b, c];
}
console.log('Result: ', destroyer([1, 2, 3, 1, 2, 3], 2, 3));
try use arguments
example
function destroyer(){var arr = []; arr.push(arguments[1]);arr.push(arguments[2]); return arr};

Iteratively adding up first and last numbers in an array

I'm trying to write a function that continually adds together the first and last elements of an array using forEach with array.shift() + array.pop().
The problem is that the for-loop doesn't complete the innermost numbers, and so the array is always left with 2 values inside of it.
Code:
function choreAssignment(chores) {
chores.sort(function(a, b) {return a - b});
var assignment = [];
chores.forEach(function() {
assignment.push((chores.pop() + chores.shift()));
});
return assignment.sort(function(a, b) {return a - b});
}
The above code works as expected, but it leaves the innermost two values inside the chores array.
For example if I run:
Code:
var arr = [1, 4, 7, 2, 5, 9, 4, 3];
choreAssignment(arr);
I get:
[8, 9, 10]
Ie, it adds 9 & 1, 7 & 2, 5 & 3, but it leaves [4, 4] inside the array.
I'm not sure why this is. Thank you.
Try changing the forEach to:
while (chores.length) {
assignment.push((chores.pop() + chores.shift()));
}
Note this assumes there are always an even number of elements in array

Slicing an Argument on Filter Array?

I have been doing this course for hours on free code camp, however, I found a solution that I do not understand and I am trying to put comments on each line to record as I achieve and understand it for future references and I already understand some lines but I cannot understand some parts of this code:
function destroyer(arr) {
// let's make the arguments part of the array
var args = Array.prototype.slice.call(arguments); // this would result into [[1, 2, 3, 1, 2, 3], 2, 3]
args.splice(0,1); // now we remove the first argument index on the array so we have 2,3 in this example
// I DO NOT UNDERSTAND THESE CODES BELOW
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
I already check on documentation and I find it hard to understand seems the code in this sample are very different. I would really appreciate your help!
arr in the section of code you don't understand refers to the first argument passed to the destroyer function; in this case, the array [1, 2, 3, 1, 2, 3]
arr.filter is using the Array.filter method to create a "filtered" version of the array with only those values that pass the "test" defined by function(element) { return args.indexOf(element) === -1; }
That function uses Array.indexOf to check if the sliced args array (which you correctly identified as being equal to [2, 3]) contains the given element. Because indexOf returns -1 when the element is not found, checking for that value is equivalent to checking that the specified element is NOT in the array
The result of all of this - and the return value of the function destroy - will be the array [1, 1], representing a filtered version of the array passed to destroy that contains all the values not equal to the other values passed to destroy.
Array.slice is part of the arrays prototype;
prototype methods are only accessable on instances of Classes.
var arr = ['a', 'b', 'c', 'd'];
// [] is JavaScript shorthand for instantiating an Array object.
// this means that you can call:
arr.slice(someArg1);
arry.splice(someArg2);

Is it safe to modify an array while looping it?

Is it safe to modify an array while looping it, such as push an element?
I'm using underscore each method
I recommend avoid each unless you absolutely need to cause a side effect for every item in a collection (trigger an event, print out a result, etc.). For simply modifying your collection, there are better ways.
If each added element is only the result of an individual input element, the typical functional pattern would be to flatmap the array, which can be thought of a two steps:
Using map to apply a function that for each element generates an array as a result. The overall result will be an array of arrays.
Using flatten on that array of arrays to get a one dimensional array.
Using underscore or lodash:
var origArray = [1, 2, 3, 4];
var duplicateIf3 = function (val) { return val === 3 ? [val, val] : val; };
_.flatten(origArray.map(duplicateIf3));
// -> [1, 2, 3, 3, 4]
(In typed FP, the function would have to return [val] for values that aren't 3, but flatten doesn't care--it flattens whatever you give it to one dimension.)
If the new element is dependent on all that came before it, you would probably use a reduce or fold instead, with the empty array as the initial value.
var origArray = [1, 2, 3, 4];
origArray.reduce(function (acc, val) { return acc.concat(acc).concat(val); }, []);
// -> [1, 1, 2, 1, 1, 2, 3, 1, 1, 2, 1, 1, 2, 3, 4]
(Sorry I couldn't think of a realistic example, but here every step uses the full output of all previous steps in a pretty simple way for illustrative purposes, and you can see from each value of the original array what's going on. Also note that you can make your own flatten from reduce, if you don't want to use underscore/lodash)
reduce is more general than flatmap, but both methods are able to convert an array into a larger array that some how depends on the first one.
If you're interested in learning more about this, I highly recommend checking out the free (online) Javascript Allongé, by Reg Braithwaite.
I'm not sure what you mean by safe, but you'd have to have a good reason to do it. I played around with it a bit, and here's what I got:
_.each(a, function(el) {
if (el % 2 == 0)
a.push(el + 1);
console.log(el);
});
// logs 1, 2, 3, 4, 5
console.log(a);
// logs [ 1, 2, 3, 4, 5, 3, 5 ]
In this case, there is no negative affect unless you wanted to go through the added elements, but you could find yourself in trickier situations if you're changing the specific elements:
_.each(a, function(el, index) {
a[index + 1] = el - 1;
if (el % 2 == 0)
a.push(el + 1);
console.log(el);
});
// logs 1, 0, -1, -2, -3
It would make more sense to use _.map for most use cases.

Categories

Resources