The task is:
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.
While working through it I found some Array.filter behaviour I'm struggling to understand:
function destroyer(arr) {
for (var i = 1; i<arguments.length; i++){
toDelete = arguments[i];
arr.filter(isItIn);
}
return arr;
}
function isItIn(item, undefined, array){
return item!=toDelete;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
My intent here was to iterate through items 1+ of the arguments, calling arr.filter each time. Arr.filter then calls isItIn which checks if the currently examined item is the one I'm searching for. However, arr is being returned unchanged. When I change isItIn to:
function isItIn(item, undefined, array){
return item==1;
}
to test, it is still unchanged, however console.logs in the original writing of isItIn show that it is receiving the arguments correctly (or so far as I've thought to determine at least.
Please note, I've solved the problem through another route, I'm not looking for a solution to the problem, merely an explanation of where my initial code went wrong.
Basically you use Array#filter and omit the result of it.
You need to assign the result of filter to the former array.
arr = arr.filter(isItIn);
function destroyer(arr) {
for (var i = 1; i < arguments.length; i++) {
toDelete = arguments[i];
arr = arr.filter(isItIn);
}
return arr;
}
function isItIn(item) {
return item != toDelete;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
You have to assign the returned array:
function destroyer(arr) {
for (var i = 1; i<arguments.length; i++) {
toDelete = arguments[i];
arr = arr.filter(isItIn);
}
return arr;
}
Related
I was trying to solve an algorithm challenge which required;
Drop the elements of an array (first argument), starting from the
front, until the predicate (second argument) returns true.
The second argument, func, is a function you'll use to test the first
elements of the array to decide if you should drop it or not.
Return the rest of the array, otherwise return an empty array.
Though I have been able to come up with a lengthy solution to this through looping the array I was wondering if there is a way to implement the break statement inside the methods.
Could it be accomplish by redefining the Array.prototype.filter method to accept a break statement ?
Though the solution could have been easy as such the methods of arrays in JavaScript doesn't accept this. How do you bypass that?
function dropElements(arr, func) {
return arr.filter(func);
}
You can just use for loop and when function returns true you can just break loop and return results from that index.
var arr = [1, 2, 3, 4, 5, 6, 7, 8];
function drop(data, func) {
var result = [];
for (var i = 0; i < data.length; i++) {
var check = func(data[i]);
if (check) {
result = data.slice(i);
break;
}
}
return result;
}
var result = drop(arr, e => e == 4)
console.log(result)
You can also use findIndex() and if match is found you can slice array from that index otherwise return empty array.
var arr = [1, 2, 3 ,4 ,5 ,6 ,7, 8];
var index = arr.findIndex(e => e == 4)
var result = index != -1 ? arr.slice(index) : []
console.log(result)
I'm trying to write a recursive function that takes an array with nested arrays and puts all the values into a single array. Right now it works sometimes, but sometimes it doesn't.
function steamrollArray(arr) {
var newArr = [];
var func = function(array){
for(i=0; i<array.length; i++){
if(Array.isArray(array[i])){
func(array[i]);
}
else {
newArr.push(array[i]);
}
}
};
func(arr);
return newArr;
}
When I run steamrollArray([1, [2], [3, [[4]]]]) it works, but if I run steamrollArray([[1], [[2]], [3, [[4]]]]); it doesn't include the 2 for some reason, and if I run steamrollArray([1, [], [3, [[4]]]]) my browser crashes. Thanks for any insight you can give!
Your problem is the i is in the global scope, you need to declare it local, so add let i
function steamrollArray(arr) {
var newArr = [];
var func = function(array){
for(let i=0; i<array.length; i++){
if(Array.isArray(array[i])){
func(array[i]);
}
else {
newArr.push(array[i]);
}
}
};
func(arr);
return newArr;
}
console.log(steamrollArray([[1], [[2]], [3, [[4]]]]));
console.log(steamrollArray([1, [], [3, [[4]]]]));
Otherwise every time you call the function the i increases and you can go out of bond (like in your last example) or skipping some element (like in the second example)
function destroyer(arr) {
for(var i=1;i<=arguments.length;i++){
arguments[0]=arguments[0].filter(function(element){
return element!==arguments[i];
});
}
return arguments[0];
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
I am trying to make a function that is supposed to accept some arguments. The first of these arguments is an array of number /or whatever. Then the rest of these arguments are supposed to be the things you want to eliminate in that array.
So for example, destroyer([1,2,3,1,2,3],2,3) should return [1,1]. The order doesn't change, which is what .filter() function supposed to do.
I read the MDN dev. page on arguments object. But I am not familiar yet with object oriented programming in Javascript. But I think for this function, I only need to know how to access the given arguments.
When I did this, my compiler shows two errors messages. 1.Bad assignment 2.Don't make function within a loop. But I don't know how to interpret these messages. Please help me.
You can use rest parameter, .indexOf() to filter elements that are not parameters passed after array parameter
function destroyer(arr, ...not) {
return arr.filter(function(el) {
return not.indexOf(el) === -1
})
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
You problem is the access of arguments in a nested function.
Basically you have this structure
function destroyer() {
// ...
arguments[0].filter(
function(element) { // <-----------------------------+
return element !== arguments[i] // |
// ^^^^^^^^^^^^ this points to --+
}
);
// ...
}
and arguments[i] is pointing in the callback for filter to the callback.
You could save the value of arguments[i] in a variable, which works as a closure for the inner function.
function destroyer() {
var result = arguments[0],
testValue, i;
for (i = 1; i < arguments.length; i++){
testValue = arguments[i]; // save value
result = result.filter(function(element) {
return element !== testValue; // use value
});
}
return result;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm trying to solve a freeCodeCamp exercise with this goal:
Write a function that takes two or more arrays and returns a new array
of unique values in the order of the original provided arrays.
In other words, all values present from all arrays should be included
in their original order, but with no duplicates in the final array.
The unique numbers should be sorted by their original order, but the
final array should not be sorted in numerical order.
So what I do is concatenate all the arguments into a single array called everything. I then search the array for duplicates, then search the arguments for these duplicates and .splice() them out.
So far everything works as expected, but the last number of the last argument does not get removed and I can't really figure out why.
Can anybody please point out what I'm doing wrong? Please keep in mind that I'm trying to learn, so obvious things probably won't be obvious to me and need to be pointed out. Thanks in advance.
function unite(arr1, arr2, arr3) {
var everything = [];
//concat all arrays except the first one
for(var x = 0; x < arguments.length; x++) {
for(var y = 0; y < arguments[x].length; y++) {
everything.push(arguments[x][y]);
}
}
//function that returns duplicates
function returnUnique(arr) {
return arr.reduce(function(dupes, val, i) {
if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
dupes.push(val);
}
return dupes;
}, []);
}
//return duplicates
var dupes = returnUnique(everything);
//remove duplicates from all arguments except the first one
for(var n = 1; n < arguments.length; n++) {
for(var m = 0; m < dupes.length; m++) {
if(arguments[n].hasOwnProperty(dupes[m])) {
arguments[n].splice(arguments[n].indexOf(dupes[m]), 1);
}
}
}
//return concatenation of the reduced arguments
return arr1.concat(arr2).concat(arr3);
}
//this returns [1, 3, 2, 5, 4, 2]
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);
Looks like you overcomplicated it a bit ;)
function unite() {
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
}
res = unite([1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]);
document.write('<pre>'+JSON.stringify(res));
Explanations
We split the problem into two steps:
combine arguments into one big array
remove non-unique elements from this big array
This part handles the first step:
[].concat.apply([], arguments)
The built-in method someArray.concat(array1, array2 etc) appends given arrays to the target. For example,
[1,2,3].concat([4,5],[6],[7,8]) == [1,2,3,4,5,6,7,8]
If our function had fixed arguments, we could call concat directly:
function unite(array1, array2, array3) {
var combined = [].concat(array1, array2, array3);
// or
var combined = array1.concat(array2, array3);
but as we don't know how many args we're going to receive, we have to use apply.
someFunction.apply(thisObject, [arg1, arg2, etc])
is the same as
thisObject.someFunction(arg1, arg2, etc)
so the above line
var combined = [].concat(array1, array2, array3);
can be written as
var combined = concat.apply([], [array1, array2, array3]);
or simply
var combined = concat.apply([], arguments);
where arguments is a special array-like object that contains all function arguments (actual parameters).
Actually, last two lines are not going to work, because concat isn't a plain function, it's a method of Array objects and therefore a member of Array.prototype structure. We have to tell the JS engine where to find concat. We can use Array.prototype directly:
var combined = Array.prototype.concat.apply([], arguments);
or create a new, unrelated, array object and pull concat from there:
var combined = [].concat.apply([], arguments);
This prototype method is slightly more efficient (since we're not creating a dummy object), but also more verbose.
Anyways, the first step is now complete. To eliminate duplicates, we use the following method:
combined.filter(function(elem, index) {
return combined.indexOf(elem) === index;
})
For explanations and alternatives see this post.
Finally, we get rid of the temporary variable (combined) and chain "combine" and "dedupe" calls together:
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
using the 3rd argument ("this array") of filter because we don't have a variable anymore.
Simple, isn't it? ;) Let us know if you have questions.
Finally, a small exercise if you're interested:
Write combine and dedupe as separate functions. Create a function compose that takes two functions a and b and returns a new function that runs these functions in reverse order, so that compose(a,b)(argument) will be the same as b(a(argument)). Replace the above definition of unite with unite = compose(combine, dedupe) and make sure it works exactly the same.
You can also try this :
var Data = [[1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]]
var UniqueValues = []
for (var i = 0; i < Data.length; i++) {
UniqueValues = [...new Set(UniqueValues.concat(Data[i]))]
}
console.log(UniqueValues)
So, I'm trying to match 2 different arrays. If the same cells match, I want to remove that cell from one array using the .slice method.
Edit: What I'm trying to do is remove a number from array1 if array2 contains a matching number. The way the code works now is that it only deletes 1 entry. I want all the entries deleted from the first array.
array1 = [1, 2, 4, 5, 7, 10];
array2 = [1,2,4,5,6,7,8];
var misc = function deleteValues(array, arayy) {
for(var i = 0; i < array.length; i++) {
if ( arayy[i] == array[i]) {
array.splice(i, 1);
}
}
return array;
};
I try to run this and under console log, the array1 is unchanged. It seems like the splice method isn't removing any cells. I searched SE, but couldn't find anything that could help me.
jsFiddle Demo
The problem is that you are modifying one of the arrays as you iterate, but you are still using the same index. The end result is that you end up comparing the wrong indexes to each other after the first removal. Use two indexes, have one offset back down when it removes an item, and have the other simply iterate.
var misc = function deleteValues(array, arayy) {
for(var i = 0, j = 0; i < array.length; i++, j++) {
if ( arayy[j] == array[i]) {
array.splice(i--, 1);
}
}
return array;
};
It seems you want to remove items from the first array if the values are also in the second. The reduceRight method seems suitable as it iterates from right to left over the array, hence removing items doesn't affect the index of subsequent elements in the array. The same result can be achieved with a decrementing loop.
Also, I think function declarations are better than assignment of expressions, but each to their own.
var array1 = [1, 2, 4, 5, 7, 10];
var array2 = [1,2,4,5,6,7,8];
function deleteValues(arr0, arr1) {
arr0.reduceRight(function(x, value, index) {
if (arr1.indexOf(value) != -1) {
arr0.splice(index, 1);
}
},'');
// Not necessary but handy for chaining
return arr0;
}
document.write(deleteValues(array1, array2));
Using an arrow function, the above can be reduced to:
function deleteValues(arr0, arr1) {
arr0.reduceRight((x, v, i) => arr1.indexOf(v) != -1? arr0.splice(i, 1):'');
return arr0;
}