Bonfire Seek and Destroy- Freecodecamp Challenge - javascript

You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
I've these instructions:
destroyer([1, 2, 3, 1, 2, 3], 2, 3) should return [1, 1].
destroyer([1, 2, 3, 5, 1, 2, 3], 2, 3) should return [1, 5, 1].
destroyer([3, 5, 1, 2, 2], 2, 3, 5) should return [1].
destroyer([2, 3, 2, 3], 2, 3) should return [].
destroyer(["tree", "hamburger", 53], "tree", 53) should return ["hamburger"].
I've found code:
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
args.splice(0,1);
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
My questions:
Can you explain this code in English, please?
Can you give a shortcut code for above challenge? please.

I've done this challenge using filter function, though t is recommended to use also 'indexOf' in order to compare value in array by the value to filter with.
````
function destroyer(arr) {
// Remove all the values
var temp = [];
for (var i = 1; i < arguments.length; i++) {
temp.push(arguments[i]);
arr = arguments[0].filter(function(value) {
return ( value !== temp[i - 1]) ;
});
}
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
````

function destroyer(arr) {
var args = arr.slice.call(arguments);
args.splice(0,1);
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
line 1 declares the overall function used for the challenge
line 2 converts the arguments of arr into a array variable named args, although this method doesn't work when optimizing in certain javascript platforms (optimization is basically what you're trying to do with your second question)
line 3 removes zero (0) and one (1) from args
lines 4 and 5 return the final result (true/false) of filtering arr for falsy and truthy by seeing whether or not an element of arg is present in arr, returning -1 if not and the element's location in arr if true
this is actually relatively optimized; there is much more more-optimized code but oftentimes it is only viable on certain javascript platforms (mozilla javascript, etc.)

Related

Javascript VArgs - not understanding arguments objects with unknown input

So this is a bit of a newbie troubleshooting question. I am doing an exercise of freecodecamp, and I am having an issue parsing the input to my function. It's short, and I think I can cut to the chase if I just show you the code:
function destroyer(arr) {
// Remove all the values
console.log("---");
console.log("arr: " + arr);
var args = Array.from(arr);
console.log(args);
var in_i = arr[0];
return in_i.filter(function (x) {
if (args.indexOf(x) !== -1) {
return true;
} else {
return false;
}
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
which gives me in the console (and I think this is the strange part):
---
arr: 1,2,3,1,2,3
[1, 2, 3, 1, 2, 3]
Clearly I'm not understanding something about arguments objects, or else something is broken. In my experience, the latter is exceedingly uncommon. I would have expected the Array.from(arr) to give an array object: [[1, 2, 3, 1, 2, 3], 2, 3].
The function function destroyer(arr) accepts only 1 parameter in the function destroyer which is an array [1, 2, 3, 1, 2, 3] and ignore the other arguments 2, 3. So, the arr is [1, 2, 3, 1, 2, 3].
If you need to access all the parameters passed to the function, then you can make use of arguments object which is an array like object. arguments would point to the array [[1, 2, 3, 1, 2, 3], 2, 3]. Following code should display the Arguments passed.
function destroyer(arr, param2, param3) {
// Remove all the values
console.log(arguments);
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
If your function takes 3 parameters as shown below, param2, param3 have been added, then you can access the value 2, 3 inside your function.
function destroyer(arr, param2, param3) {
// Remove all the values
console.log("---");
console.log("arr: " + arr);
var args = Array.from(arr);
console.log(args);
var in_i = arr[0];
return in_i.filter(function (x) {
if (args.indexOf(x) !== -1) {
return true;
} else {
return false;
}
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
In fact, the part that confused me was that on MDN, the use of the arguments variable was not just illustrative, it's a built-in variable. After #Agalo pointed out that the values I was getting on the console were just the array, it clicked for me.
The solution was to access the extra arguments through the built-in arguments object which automatically has the (reserved) name arguments. Code is like so (for completeness, I should note that I also had false and true switched in the return statements):
function destroyer(arr) {
// Remove all the values
var args_l = arguments.length;
var args = Array.from(arguments);
console.log(args);
var in_i = args.shift();
return in_i.filter(function (x) {
if (args.indexOf(x) !== -1) {
return false;
} else {
return true;
}
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
To riff off of Agalo, you'll note you don't actually need the param2, param3 arguments in the function definition to get the exact same output:
function destroyer(arr) {
// Remove all the values
console.log(arguments);
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
At the risk of putting too fine a point on it:
function args_tester(a) {
console.log("a: " + a);
console.log("arguments: " + arguments);
console.log("arguments_as_array: " + Array.from(arguments));
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
args_tester("a", "b", "c");

What's the most concise way in javascript to find consecutive dups in an array, sum them up and reiterate?

Example
I have an array of:
const myArr = [1, 1, 1, 3, 2, 2, 4, 3];
If I were pass it to the function it should produce the output like so:
//result: [2, 1, 3, 8, 3]
The consecutive first two 1s and 2s were consecutive duplicates so they were summed up.
The first two consecutive 1s became 2 so it now doesn't match.
The consecutive 2s became 4 so it matched the next running number and summed up again resulting in an 8
Although the 3s are duplicate they we're not summed up because they're not consecutive
Bonus
It would be great if someone can demonstrate this using javascript's array functions map or reduce.
Is this possible without any kind of recursion? someone mentioned its possible, then I'd be nice if it didn't use recursion
EDIT
Adding this here because some have already provided answers.
In the case of:
const myArr = [1, 1, 1, 1];
// result: [4]
const myArr = [1, 1, 1, 1, 3, 2, 2, 4, 3];
// result: [4, 3, 8, 3]
Another interesting case that didn't pass my mind:
const myArr = [1, 1, 2, 2];
//result [4, 2] instead of [2,4]
It should check against the new array as soon a change was made
With the assumption that this will be used only on arrays of numbers, this is simple enough to do with reduce.
function duplicateAddition(input) {
var last;
var result = input;
do {
last = result;
result = last.reduce(function (carry, item) {
if (carry[carry.length - 1] == item) {
carry[carry.length - 1] *= 2;
} else {
carry.push(item);
}
return carry;
}, []);
} while (last.length != result.length);
return result;
}
Assuming the array always contains numbers, the most concise I can think of would be
function merge(arr) {
var x = String(arr),
y = x.replace(/([^,]),\1/, (_,m)=>2*m);
return x.length == y.length ? y.split(",").map(Number) : merge(y);
}
If you want to avoid the tail recursion, it's trivial to convert that to a loop:
function merge(arr) {
var y = String(arr);
do {
var x = y;
y = x.replace(/([^,]),\1/, (_,m)=>2*m);
} while(x.length != y.length)
return y.split(",").map(Number);
}
or
function merge(arr) {
for (var x=String(arr), y=""; x.length!=y.length; x=y.replace(/([^,]),\1/, (_,m)=>2*m))
y=x;
return x.split(",").map(Number);
}
Btw, to alter the behaviour of the algorithm to get [2, 4] instead of [4, 2] from [1, 1, 2, 2] (i.e. find all duplicates and sum them before reiterating), you'd only need to add the /global flag to the regex.
This solution uses a recursive generator. It has the advantage that it pretty much says what it means and does what it says.
function* x([first, second, ...rest]) {
if (first !== undefined) {
if (first !== second) yield first; else second *= 2;
yield* x([second, ...x(rest)]);
}
}
function test(a) { console.log(Array.from(x(a))); }
test([1, 1, 1, 3, 2, 2, 4, 3]);
test([1, 1, 1, 1]);
test([1, 1, 2, 2]);
In this solution, the input [1, 1, 2, 2] produces [2, 4]. That is because the logic greedily collapses the 2, 2 into a 4, which then does not match the 2 resulting from 1, 1. If this behavior is not desired, then it might suffice to change the line above to:
yield* x([second, ...rest]);
This is a simple game of unshifting from array a to pushing array b. So if a[0] === a[1] you take the first two items from array a, get their sum and push it into array b but if a[0] !== a[1] then take only the first item and push it into array b. However before we push the interim value (t in below snippet) we check the last item of array b to see if it's equal to the interim. If they are equal we just double the last item of array b if not just push interim (t) over b.
Then make a tail call optimized recursive invoking.
function sumAndReduce(a, b = []){
var t = 0;
if (!a.length) return b;
t = a[0] === a[1] ? a.splice(0,2).reduce((p,c) => p+c)
: a.splice(0,1)[0];
b[b.length-1] === t ? b[b.length-1] += t
: b.push(t);
return sumAndReduce(a,b);
}
var myArr = [1, 1, 1, 3, 2, 2, 4, 3],
result = sumAndReduce(myArr);
console.log(JSON.stringify(result));
console.log(JSON.stringify(sumAndReduce([1, 1, 1, 1])));
console.log(JSON.stringify(sumAndReduce([1, 1, 2, 2])));
Well... if you don't want recursion even if it is a tail call optimized one, then it's very trivial to convert the above code into an iterative one with a while loop. I'll leave it to you..!
So i can not make sure if your request as per an input [1,1,2,2] breaks the initial conditions but here is a modified version of the above code to comply with that as well.
function sumAndReduce(a, b = []){
var t = 0;
if (!a.length) return b;
t = a[0] !== a[1] || b[b.length-1] === a[0] ? a.splice(0,1)[0]
: a.splice(0,2).reduce((p,c) => p+c);
b[b.length-1] === t ? b[b.length-1] += t
: b.push(t);
return sumAndReduce(a,b);
}
var myArr = [1, 1, 1, 3, 2, 2, 4, 3],
result = sumAndReduce(myArr);
console.log(JSON.stringify(result));
console.log(JSON.stringify(sumAndReduce([1, 1, 1, 1])));
console.log(JSON.stringify(sumAndReduce([1, 1, 2, 2])));

Remove all elements from the initial array that are of the same value as the arguments followed by the initial array

There is an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
Here is my code, but I am unable to solve the problem.
function destroyer(arr) {
// First I have converted the whole input into an array including the arguments
var args = Array.prototype.slice.call(arguments);
var abc=args.splice(0,1);//Here I have taken out the array part i.e[1,2,3,1,2,3] and also now args contain 2,3 only
function des(abc){
for(var i=0;i<abc.length;i++){//I tried to check it for whole length of abc array
if(args.indexOf(abc)===-1){
return true; //This should return elements of [1,2,3,1,2,3] which are not equal to 2,3 i.e [1,1] but for string inputs it is working correctly.How to do for these numbers?
}
}
}
return arr.filter(des); //but this filter function is returning empty.How to solve my problem??
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
For destroyer(["tree", "hamburger", 53], "tree", 53) the code is giving output ["hamburger"],which is working fine.
But for destroyer([1, 2, 3, 1, 2, 3], 2, 3); code is giving no output.
You can use Array.filter. Following example depicts the same
Also destroyer([1, 2, 3, 1, 2, 3], 2, 3); in this call, [1, 2, 3, 1, 2, 3] is first argument, 2 is second and 3 is third. So arr will have be [1, 2, 3, 1, 2, 3] and not [1, 2, 3, 1, 2, 3], 2, 3
function removeElementFromArray(arr,num){
var _temp = arr.filter(function(item){
return (item !== num);
});
return _temp;
}
(function main(){
var arr = [1,1,1,2,4,3,2,4,5,4,3,1,4,5,2];
var result = removeElementFromArray(arr,1);
var result1 = removeElementFromArray(arr,3);
console.log(result, result1);
})()
Try this:
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments, 1);
function des(abc){
return args.indexOf(abc) === -1;
}
return arr.filter(des);
}
var result = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
document.getElementById('output').innerHTML = JSON.stringify(result);
<pre id="output"></pre>
Lodash has without method that can be useful.
link to doc.
From lodash docs:
_.without(array, [values])
Creates an array excluding all provided values using SameValueZero for
equality comparisons.
Example:
_.without([1, 2, 1, 3], 1, 2);
// → [3]
This should work for you:
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
var abc = args.splice(0, 1);
function des(value) {
// you don't need loop here, just check if given value is in args array
return args.indexOf(value) === -1;
}
// splice returns array of deleted elements so that your first argument is abc[0]
return abc[0].filter(des);
}
var result = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
console.log(result);
Try this easy code:
function destroyer(arr) {
/* Put all arguments in an array using spread operator and remove elements
starting from 1 */
const args = [...arguments].splice(1);
/* Check whether arguments include elements from an array and return all that
do not include(false) */
return arr.filter(el => !args.includes(el));
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3); // [1, 1]

Filtering out specific values from an array

destroyer([1, 2, 3, 1, 2, 3], 2, 3) should return [1, 1], but it returns [1, 2, 3, 1, 2, 3]. What is wrong with this code?
function destroyer(arr) {
// Remove all the values
var arg_num = arguments.length;
var new_arr = arguments[0].filter(function(a){
for (var i = 1; i < arg_num; i++){
if (a === arguments[i]) a = false;
}
return a;
});
return new_arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
argument is used to get the list of arguments of current function. While performing filter you used argument, it gives argument of filter method. it differ from the destroyer method argument.
So store the destroyer method argument in one variable.
Try this code.
function destroyer(arr) {
// Remove all the values
var args = arguments;
var arg_num = args.length;
var flag;
var new_arr = arguments[0].filter(function (a) {
flag = false;
for (var i = 1; i < arg_num; i++) {
if (a === args[i]) {
flag = true;
break;
};
}
if (flag)
return false;
else
return true;
});
return new_arr;
}
console.log(destroyer([0, 2, 3, 0, 2, 3], 0, 3));
Hope this will help you.
You can instead use difference function from lodash library. It's a well tested and widely used utility library.
var _ = require('lodash');
var result = _.difference([1, 2, 3, 1, 2, 3], [2, 3])); // returns [1, 1]
The problem is that the arguments keyword is usually bound to the current function, which in this case is the anonymous function used in filter.
ES6 allows to fix this easily by introducing arrow functions, which don't bind arguments.
(function destroyer(arr) {
var arg_num = arguments.length;
return arguments[0].filter(a => {
for (var i = 1; i < arg_num; i++){
if (a === arguments[i]) return false;
}
return true;
});
})([1, 2, 3, 1, 2, 3], 2, 3); // [1, 1]
However, note this function has cost n m where n is the number of elements in the array and m is the number of additional arguments. But could be better.
For example, if the additional arguments will always be numbers, you can sort them, and then use dichotomic searches. This will cost n lg(m).
Another approach would be using a hash table. It will still cost n m on the worst case, but only n on average.
If you don't want to implement that manually, you can use a ES6 set. Its specific cost will be implementation dependent, but is required to be sublinear on average.
(function destroyer(arr, ...args) {
var s = new Set(args);
return arr.filter(a => !s.has(a));
})([1, 2, 3, 1, 2, 3], 2, 3); // [1, 1]

JavaScript filter callback that uses arguments

The goal is to filter an array and remove all occurrences of elements specified in its argument list.
For example, given removeElements([1, 2, 3, 1, 2, 3,4], 2, 3), my output should be [1,1,4].
function removeElements(arr) {
//I got an error that says **functions** not allowed **inside loop**
for(var i=1;i<arguments.length;i++){
arr= arr.filter(function(e){
return e!==arguments[i];
});
}
return arr;
}
Second thing I tried is moving the filter out of the for loop.
function removeElements(arr) {
function isNotEqual(e){
return e!==this;
}
for(var i=1;i<arguments.length;i++){
arr= arr.filter(isNotEqual,arguments[i]);
}
return arr;
}
None of them work. It always return arr as [1,2,3,1,2,3,4].
Can you please tell as to what is wrong in my usage? Or what is the approach for using filter in this scenario?
You can use Array.prototype.slice to get the blacklisted elements in array form.
Then use Array.prototype.indexOf to see if a given element is in the array for the filter function.
http://jsfiddle.net/Loothof7/
function removeElements(arr) {
var blacklist = Array.prototype.slice.call(arguments, 1);
return arr.filter(function(e) {
return blacklist.indexOf(e) == -1;
});
}
alert(removeElements([1, 2, 3, 1, 2, 3,4], 2, 3));
Note that Function.prototype.call is used on Array.prototype.slice with the this scope argument of arguments instead of directly calling arguments.slice since arguments isn't actually a "real" array.
To try to explain the reasons the snippets didn't succeed:
Every function defines its own arguments, even when the function is embedded.
function removeElements(arr) {
console.log(arguments);
// Arguments {
// 0: Array [1, 2, 3, 1, 2, 3, 4],
// 1: 2,
// 2: 3
// }
arr = arr.filter(function (e) {
console.log(arguments);
// Arguments {
// 0: 1, 2, 3, 1, ... (each value in `arr`)
// 1: 0, 1, 2, 3, ... (each index)
// 2: Array [1, 2, 3, 1, 2, 3, 4] (`arr` itself)
// }
// ...
});
return arr;
}
removeElements([1, 2, 3, 1, 2, 3, 4], 2, 3);
By retrieving values from arguments inside of the iterator (function(e) {...}), the statement will compare e against values in the 2nd Arguments.
for(var i=1;i<arguments.length;i++){
arr = arr.filter(function(e){
// 1st = 0 (the first index from `arr`)
// 2nd = [1, 2, 3, ...] (the `arr` itself)
console.log(arguments[i]);
return e!==arguments[i];
});
}
One option to resolve this is to access arguments outside of the iterator function, stashing the value in a variable that won't have the same conflict:
for(var i=1;i<arguments.length;i++){
var skip = arguments[i];
arr = arr.filter(function (e) {
return e !== skip;
});
}
http://jsfiddle.net/y7evq6nq/
If you're not using strict mode, the value of this will always be an Object.
When you provide a primitive value for a thisArg, it will be boxed into its equivalent Object type. In this case, a new Number.
function foo() {
console.log(typeof this, this); // 'object' Number(3)
return true;
}
[0].filter(foo, 3);
And, since === first checks for type equality, a primitive and boxed number cannot be equal:
var number = 3;
var boxedNumber = new Number(3);
console.log(typeof number); // 'number'
console.log(typeof boxedNumber); // 'object'
console.log(typeof number === typeof boxedNumber); // false
console.log(number === boxedNumber); // false
You can use the .valueOf() method to retrieve the primitive value from the object.
function isNotEqual(e){
return e!==this.valueOf();
}
http://jsfiddle.net/ow9b78bf/
Or, you can try using strict mode, which allows this to hold a primitive value without boxing it.
arguments are function specific pseudo-variable. Using it inside callback will give arguments of callback and not outer function.
function removeElements(arr) {
var args = Array.prototype.slice.call(arguments);
for(var i=1;i<args.length;i++){
arr= arr.filter(function(e){
return e!==args[i];
});
}
}
I have already answered this type of question here any way I post it to you
A simple function
function filter(){
var j = -1;
for(var i = 1; i < arguments.length; i++){
j = arguments[0].indexOf(arguments[i]);
if(j > -1){
arguments[0].splice(j, 1);
}
}
return arguments[0];
}
you can call this function with no of args eg:
filter([1,2,3,4,5,6,7,8,9], 1, 3, 5); //return [2,4,6,7,8,9]
filter([1,2,3,4,5,6,7,8,9], 1); //return [2,3,4,5,6,7,8,9]
This will use a callback function to check against the two numbers given in the arguments list.
function removeElements(arr, num1, num2){
return arr.filter(numChecks(num1, num2));
}
function numChecks(num1, num2){
return function(element){
return element !== num1 && element !== num2;
}
}
removeElements([1, 2, 3, 1, 2, 3,4], 2, 3)
I think this is what you want to do, maybe I'm wrong.
var result = [1, 2, 3, 1, 2, 3, 4].filter(function(item) {
return item !== 1 && item !== 2;
});
console.log(result); // [3, 3, 4]
UPDATE:
This function can do the job you want passing the items to be remove as an array instead of turning the arguments[1] param an Array using slice.call():
function removeItems(arr, items) {
return arr.filter(function (elem) {
return items.indexOf(elem) === -1;
});
}
var result = removeItems([1, 2, 3, 1, 2, 3, 4], [2, 3]);
console.log(result); // [1, 1, 4]
There are really good answers here, but you can do it very clean in this way, remember you have an objects option in filter method which you can use in the callback function, in this case i'm using it like : arguments[i] so I can check every value in the arguments array
function destroyer(arr) {
for(var i = 1; i < arguments.length; i++){
arr = arr.filter(isIn, arguments[i]);
}
function isIn(element,index, array){
if (element != this){
return element;
}
}
return arr;
}

Categories