Javascript filter vs map problem - javascript

As a continuation of my min/max across an array of objects I was wondering about the performance comparisons of filter vs map.
So I put together a test on the values in my code as was going to look at the results in FireBug.
This is the code:
var _vec = this.vec;
min_x = Math.min.apply(Math, _vec.filter(function(el){ return el["x"]; }));
min_y = Math.min.apply(Math, _vec.map(function(el){ return el["x"]; }));
The mapped version returns the correct result. However the filtered version returns NaN. Breaking it out, stepping through and finally inspecting the results, it would appear that the inner function returns the x property of _vec but the actual array returned from filter is the unfiltered _vec.
I believe my usage of filter is correct - can anyone else see my problem?
Here's a simple test:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>S:GTC Map Test</title>
</head>
<body>
<script type="text/javascript">
function vector(x,y,z) { this.x = x; this.y =y; this.z=z; }
var vec = [];
vec.push(new vector(1,1,1));
vec.push(new vector(2,2,2));
vec.push(new vector(2,3,3));
var _vec = vec;
min_x = Math.min.apply(Math, _vec.filter(function(el){ return el["x"]; }));
min_y = Math.min.apply(Math, _vec.map(function(el){ return el["x"]; }));
document.write("<br>filter = " + min_x);
document.write("<br>map = " + min_y);
</script>
</body>
</html>

No, the filter method doesn't return the unfiletered array. It returns an array containing the items where the inner function returns true.
As you are not returning a boolean value from the inner function, the value is converted to boolean, so the object reference is converted to true. Thus, it returns a new array that contains all the items from the original array.
The filter method doesn't do the same as the map method. The map method is used to convert each item of an array, while the filter method is used to select certain items of an array. Comparing the performance between the methods is moot, as only one of them does what you want to do.

Quoted from:
JavaScript: The Definitive Guide
by David Flanagan
map()
The map() method passes each element of the array on which it is
invoked to the function you specify, and returns an array containing
the values returned by that function.
For example:
a = [1, 2, 3];
b = a.map(function(x) { return x*x; }); // b is [1, 4, 9]
The function you pass to map() is invoked in the same way as a
function passed to forEach(). For the map() method, however, the
function you pass should return a value.Note that map() returns a new
array: it does not modify the array it is invoked on. If that array is
sparse, the returned array will be sparse in the same way: it will
have the same length and the same missing elements.
filter()
The method returns an array containing a subset of the elements of the
array on which it is invoked. The function you pass to it should be
predicate: a function that returns true or false. The predicate is
invoked just as for forEach() and map(). If the return value is true ,
or a value that converts to true, then the element passed to the
predicate is a member of the subset and is added to the array that
will become the return value.
Examples:
a = [5, 4, 3, 2, 1];
smallvalues = a.filter(function(x) { return x < 3 }); // [2, 1]
everyother = a.filter(function(x,i) { return i%2==0 }); // [5, 3, 1]

// MAP creates a new array
// MPA return new Array
var arr = [1, 2, 3, 4, 5, 6, 7];
var newArr = arr.map((el) => {
return el * 2;
});
console.log(newArr); //2,4,3,8,10,12,14
// filter() return new Array
var newFilter = arr.filter((el) => {
return el * 2;
});
console.log(newFilter); // 1,2,3,4,5,6,7
now you can see I have return el*2 both for map and filter they are giving a different output
Filter()
The filter() method creates a new array filled with all array elements that pass a test implemented by the function.
If this conditional returns true, the element gets pushed to the output array. If the condition returns false, the element does not get pushed to the output array.
var arr = [1, 2, 3, 4, 5, 6, 7];
var newFilter = arr.filter((el) => {
return el > 3;
});
console.log(newFilter); //[1, 2, 3, 4]
Map()
The map() method is used for creating a new array from an existing one, applying a function to each one of the elements of the first array
var arr = [1, 2, 3, 4, 5, 6, 7];
var newArr = arr.map((el) => {
return el * 2;
});
console.log(newArr); //2,4,3,8,10,12,14

Related

How can i target elements in another array via .filter()

Im trying to return the values in argument[0] that are not equal to the values of arguments[1] and so on.
Ive created a variable 'let argArr' that holds the values of the arguments after the first, Im trying to understand why in my .filter() i cannot target 'let argArr'?
function destroyer(arr) {
let argArr = [];
for (let i = 1; i < arguments.length; i++) {
argArr.push(arguments[i])
}
return arr.filter(i => i !== argArr)
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
You are comparing an arr array's element to the argArr what is wrong.
The right way is to check if element is inside argArr and filter if yes.
return arr.filter(i => !argArr.includes(i))
Instead of using arguments explicitly specify the array as the first argument, and then use rest parameters to gather all the remaining arguments into an array. Then use filter to return only those elements in the array that are not in the rest array.
function destroyer(arr, ...rest) {
return arr.filter(el => !rest.includes(el));
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
Use spread operator to treat arguments as normal array
function foo() {
return [...arguments].filter(el => el);
}
console.log(foo(1,2,3))

How do I return a new length of the array in JavaScript

Write a function called push which accepts two parameters, an array and any value.
The function should add the value to the end of the array and then return the new length of the array.
Do not use the built in Array.push() function!
Examples:
var arr = [1, 2, 3];
push(arr, 10); // 4
arr; // [1, 2, 3, 10]
My question is How to return a new array with the new length.
You can try this out . Get the length of the existing array and put the new number at arr[length] index of it . See the code below:
function push(arr,num){
var length = arr.length;
arr[length]=num;
return arr;
}
you could do it with something like this:
const push = (a, v) => (a[a.length] = v,a);
array = [1,2,3]
console.log(push(array, 4));

why does the first console.log() print undefined values but second one has transformed values

Why does the first console.log() print undefined values but second one has transformed values? I know it has to do something with function scope but not getting it
var array = [1,2,3,4,5,7];
function incrementByOne(arr) {
arr = arr.map(function(value, index, array){
arr[index] = arr[index] +1;
});
console.log(arr);
}
incrementByOne(array);
console.log(array);
// [undefined, undefined, undefined, undefined, undefined, undefined]
// [2, 3, 4, 5, 6, 8]
also i notice that the first console.log() knows how many times to iterate but what happens to the value...
js bin link
you need to return the incremented value from the function inside map. use return arr[index] +1
Also you need to return the new array formed using map and stored in arr now.
var array = [1,2,3,4,5,7];
function incrementByOne(arr) { //contains array reference
arr = arr.map(function(value, index, array){
return value +1;
});
//now arr contains a new array and doesn't refer to passed array anymore.
console.log(arr);
return arr;
}
array = incrementByOne(array);
console.log(array);
// [undefined, undefined, undefined, undefined, undefined, undefined]
// [2, 3, 4, 5, 6, 8]
if you don't want to return, you can use forEach(), as in that case arr will refer to passed array throughout. The difference is because map returns a new array.
var array = [1,2,3,4,5,7];
function incrementByOne(arr) { //contains array reference
arr.forEach(function(value, index){
arr[index] = value +1;
});
//arr still refers to the passed array.
console.log(arr);
}
incrementByOne(array);
console.log(array);
// [undefined, undefined, undefined, undefined, undefined, undefined]
// [2, 3, 4, 5, 6, 8]
Well, all the above answers are correct but they miss the most important point here. There is a concept in JavaScript called call by sharing.
Consider this code:
var num= 3;
var json = {myValue : '10'};
var json2 = {myValue : '100'};
function callBySharing(a,b,c){
a = a + 37;
b = {myValue : 'new value'};
c.myValue = 'new Value';
}
callBySharing(num,json,json2);
console.log(num);//3 *UNCHANGED*
console.log(json.myValue);//10 *UNCHANGED*
console.log(json2.myValue);//'new Value' *CHANGED*
So what you are doing is same as what is happening in json.myvalue; You are trying to update the whole object and replace it with the new value. So a very simple change in the code with do this for you:
var array = [1,2,3,4,5,7];
function incrementByOne(arr) {
arr.map(function(value, index, array){
arr[index] = arr[index] +1;
});
console.log(arr);
}
incrementByOne(array);
console.log(array);
I just replaced the arr= arr.map().... part to just arr.map().....
What this does is, it changes the function to json2.myValue example case.
So what is the difference between the 2: JS lets you update items within the object but not the whole object.By making the above said change in code, you are updating individual values of arr and not replacing the whole object with new values. I learnt this concept from SO only back when I was confused with it. So I am linking the post(Is JavaScript a pass-by-reference or pass-by-value language?)
Hope this helps!
var array = [1,2,3,4,5,7];
function incrementByOne(arr) {
arr = arr.map(v => v + 1);
console.log(arr);
}
incrementByOne(array);
console.log(array);
// [undefined, undefined, undefined, undefined, undefined, undefined]
// [2, 3, 4, 5, 6, 8]
A couple of things going on here:
The callback you pass into the .map function should return a value with the return keyword. If you don't return a value from the callback, the default return value is undefined. That's why the 'arr' array you define in incrementByOne only has undefined values inside of it when you log it with your first console.log. I think what you're really trying to do is simply return value + 1 inside of the callback.
You're making a classic passed-by-value passed-by-reference error here; it's a mistake everyone makes when they're first learning JS. I'd recommend checking out posts like this one. In short, inside of your .map callback you are mutating the array that you passed into incrementByOne, which is the same array you log out with the second console.log; that's why the values appear to have been incremented correctly.

why are there different arguments in this javascript function

I saw this function , though it works fine but I am bit puzzled about the function expressions. Here is the code
mapForEach(arr, fn) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(fn(arr[i]))
}
return newArr;
}
can anybody explain to nme what this rather complicated code is actually doing?
Lets say you have var array = [1, 2, 3, 5]; and then run var array2 = mapForEach(array, function(i) { return i * 2; })
array2 would then contain [2, 4, 6, 10].
So it returns a new array where you have the ability to modify each record with a function
mapForEach enumerates an array and calls a supplied function on each element.
example:
var a = [1, 2, 3];
console.log(mapForEach(a, (x) => x * 2));
would create a new array with the values (and output to console):
[2, 4, 6]
Basically it is an implementation of javascript native array function map, which creates a new array with the results of calling a provided function on every element in this array.
More info about mentioned function you can find here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

When i pass arguments it consoles an Error, when i pass an empty array it consoles an empty array

When i pass arguments it consoles an Error(cannot read property "map" of undefined), when i pass an empty array it consoles an empty array :)
Array.prototype.square = function() {
return [].map((number) => number^2)
}
var numbers = [1, 2, 3, 4, 5];
console.log(numbers.square())
Use Math.pow()
Array.prototype.square = function() {
return this.map(function(item) {
return Math.pow(item, 2);
});
}
var numbers = [1, 2, 3, 4, 5];
console.log(numbers.square())
You are applying map() on an empty array so it will always return empty array, instead use this to refer the array. Use Math .pow() to get square of the array element.
Array.prototype.square = function() {
return this.map((number) => Math.pow(number, 2))
}
var numbers = [1, 2, 3, 4, 5];
console.log(numbers.square())
FYI : ^ (caret symbol) using for bitwise XOR in JavaScript, refer : What does the caret symbol (^) do in JavaScript?

Categories