The map function looks much better than a for loop:
var arr = ["Apple", "Orange"];
// map example
arr.map(function(item){ eat(item); });
// for example
for(var i=0; i<arr.length; i++){
eat(arr[i]);
}
And I would prefer a map over for but:
A function, which map becomes as an argument should have a return value to be reduces to an array which map should return. What impact has passing a viod function to a map, like in my example?
map returns an array. What happens if I don't assign it to a variable, like in my example?
Is there any void function like map?
A function, which map becomes as an argument should have a return value to be reduces to an array which map should return. What impact has passing a viod function to a map, like in my example?
The value returned by a function without a return statement is undefined. Thus, if you pass a function that never returns anything to map, it will create a new array with length equal to your original array and each value being undefined.
map returns an array. What happens if I don't assign it to a variable, like in my example?
An array is still created but it will be discarded immediately after creation and garbage collected shortly. This behavior might be optimized away by the JS engine!
Is there any void function like map?
As everyone else pointed out, there is the Array.prototype.forEach function which iterates over the array (similarly to map) but doesn't create a new array or use the result of the callback associated to it in any way.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
You're looking for .forEach().
You may find that browsing the MDN documentation is a better way to accumulate this sort of information than asking questions. For now at least I hope this answer will assuage your suffering.
edit — as to question 1, it won't hurt anything. The .map() function will build an array full of undefined entries. (I think.)
For 2, it's also OK. It is never a problem to ignore the return value of a function, at least as far as the runtime is concerned.
1. A function, which map becomes as an argument should have a return value to be reduces to an array which map should return. What impact has passing a viod function to a map, like in my example?
var numbers = [1, 4, 9];
var mapped = numbers.map(function(){
//do nothing
});
console.log(numbers);//[1, 4, 9]
console.log(mapped);//[undefined, undefined, undefined]
the initial array will remain as same and you will get an array with undefined array
2 . map returns an array. What happens if I don't assign it to a variable, like in my
example?
nothing will happen, you loose the changed array.
3.Is there any void function like map?
All array methods doesnot modify the reference array ,
EG
Array.concat(value1[value2[value...]])
Array.every(function)
Array.filter(function)
Array.forEach(function)
Array.join(delimeter)
Array.indexOf(searchStr[, startIndex])
Array.lastIndexOf(searchStr[, startIndex])
Array.map(function) .... etc
but Array.splice(), push, pop, shift, unshift,.. etc will change
What you're looking for is the forEach method.
var arr = ["Apple", "Orange"];
arr.forEach(eat);
There's the forEach array instance method.
Related
Was writing a script in JS to make some dummy data for testing my API and ran into an interesting quirk with the forEach loop in JS.
const dictionary = {};
const undefinedArray = Array(3); // [undefined, undefined, undefined]
undefinedArray.forEach((_, index) => {
console.log('Logging at index: ', index)
const someObject = { id: index };
if (!dictionary[someObject.id]) {
dictionary[someObject.id] = someObject
}
});
console.log(dictionary);
After checking the output of this snippet, you'll see that nothing inside the forEach loop is logged and the dictionary is still an empty object. I was talking with my coworker about this behaviour and he said he ran into this particular issue before and offered this as a solution.
const dictionary = {};
const undefinedArray = [...Array(3)]; // [undefined, undefined, undefined]
undefinedArray.forEach((_, index) => {
console.log('Logging at index: ', index)
const someObject = { id: index };
if (!dictionary[someObject.id]) {
dictionary[someObject.id] = someObject
}
});
console.log(dictionary);
By wrapping the Array constructor in square brackets and utilizing the spread operator, now the array is looped through and the object is built correctly. This fascinated me, so I went to the documentation for the Array object and found this:
arrayLength
If the only argument passed to the Array constructor is an integer between 0 and 2^32 - 1 (inclusive), this returns a new JavaScript array with its length property set to that number (Note: this implies an array of arrayLength empty slots, not slots with actual undefined values). If the argument is any other number, a RangeError exception is thrown.
So apparently it is not assigning each value undefined, but only setting its length property to whatever is passed in the constructor. This is not apparent when you log Array(n) to the console because it shows an array with n undefined values. I assume the toString method for the Array object is based on its length property and uses a normal for or for of loop to construct the string.
It does begin to make a little bit more sense, however, when you explicitly set an index of the newly defined array. In the snippet below, the same array is initialized, but the zero index is explicitly assigned undefined as a value. Since this is an "actual undefined value" in Mozilla's words, the forEach loop exectues at index zero.
const dictionary = {};
const undefinedArray = Array(3); // [undefined, undefined, undefined]
undefinedArray[0] = undefined
undefinedArray.forEach((_, index) => {
console.log('Logging at index: ', index)
const someObject = { id: index };
if (!dictionary[someObject.id]) {
dictionary[someObject.id] = someObject
}
});
console.log(dictionary);
Array.map() behaves the same way. So I guess my main question would be, are there other ways to execute forEach and map without filling the array or by using the quirky hack I mentioned earlier?
To recap: these are the two work arounds I've found for this particular use case:
[...Array(n)] OR Array(n).fill(). Both of these mutations to the array will allow a forEach loop to iterate over all values in the array.
So apparently it is not assigning each value undefined, but only setting its length property to whatever is passed in the constructor.
Correct. (Provided you pass only a single argument and it's a number. If you pass a non-number, or pass more than one argument, they're used as elements for the array. So Array("3") results in ["3"]; Array(3, 4) results in [3, 4].)
This is not apparent when you log Array(n) to the console because it shows an array with n undefined values.
It depends on what console you use. The devtools in Chromium browsers show (3) [empty x 3] for exactly that reason, to differentiate between empty array slots and ones containing the value undefined.
So I guess my main question would be, are there other ways to execute forEach and map without filling the array or by using the quirky hack I mentioned earlier?
If you want forEach and map to visit elements of the array, they have to actually exist. Those methods (and several others) are defined such that they don't call your callback for empty slots in sparse arrays. If by "quirky hack" you mean [...Array(3)], that's also filling the array (and is fully-specified behavior: [...x] uses the iterator x provides, and the array iterator is defined that it yields undefined for empty slots rather than skipping them as forEach, map, and similar do). Doing that (spreading the sparse array) is one way to create an array filled with undefined (not empty) elements. Array.fill is another. Here's a third: Array.from({length: 3})
const a = Array.from({length: 3});
a.forEach(value => {
console.log(`value = ${value}`);
});
Which you use is up to you. Array.from is very simple and direct. Similarly Array(3).fill(). I probably wouldn't use the spread version (just because I think it's fairly unclear to people who don't have a deep knowledge of how the array iterator works), but it's a matter of style.
I'm learning JavaScript. I wrote this code to learn the map function. But then I got confused as to why is this not mapping over it continuously as with each map sequence a new element is pushed to the array. Shouldn't it continue to push new elements as it is mapping over ? Why does the map function only run for the original three elements and not for the new pushed ones?
I tried to debug it in the node environment and the arr variable goes in a closure. I know what a closure but I'm not able to understand what is going on here.
let array = [1, 2, 3];
array.map((element) => {
array.push(10);
console.log(element);
});
I expect that the output should be 1,2,3,10,10,10,10,10,10,10,10......10
But the actual output is only 1,2,3.
To quote from MDN:
The range of elements processed by map is set before the first invocation of callback. Elements which are appended to the array after the call to map begins will not be visited by callback. If existing elements of the array are changed, their value as passed to callback will be the value at the time map visits them.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Description
So, it behaves like that because that is how it is designed. And it is designed that way, amongst other reasons, to prevent infinite loops!
map is a function from the Functional Programming world, where immutability is an important principle. According to this principle, if you call map on an input (and other variables don't change) you will always get exactly the same result. Allowing modification of the input breaks immutability.
Because it does not mutate the array (see Array.prototype.map()).
Instead, it returns a new array with the results of calling a provided function on every element in the calling array.
In the following snippet, what mutates the array is the call to array.push(10); three times (once per element in the original array) and not the map function itself.
let newArray = array.map((element) => {
array.push(10); // <-- here you mutate the array
console.log(element);
});
An important quote from the mentioned documentation (and key point here is):
The range of elements processed by map is set before the first
invocation of callback. Elements which are appended to the array
after the call to map begins will not be visited by callback.
In the following snippet you can see an example of how to properly use the map function:
let array = [1,2,3];
let newArray = array.map(element => element + 10); // sum 10 to every element
console.log('original: ', array); // original: [1,2,3]
console.log('new one: ', newArray) // new one: [11,12,13]
One last thought (from the docs too), taking as reference the code you posted:
Since map builds a new array, using it when you aren't using the returned array is an anti-pattern; use forEach or for-of instead.
Signs you shouldn't be using map:
A) You're not using the array it returns, and/or
B) You're not returning a value from the callback.
Why is exactly as seen on MDN:
var new_array = arr.map(function callback(currentValue[, index[, array]]){}
The range of elements processed by map is set before the first invocation of callback. Elements which are appended to the array after the call to map begins will not be visited by callback.
(Thank you to Joe's post above from MDN for this quote.)
Once map is called it then takes the array at that moment as it's parameter; once it's been passed then any changes are irrelevant to the previous variable itself.
See below:
let array = [1, 2, 3];
array.map((element) => {
array.push(10);
console.log(element);
});
console.log(array)
I am trying to create a function that mimics Array.prototype.push.
It takes a variable number of arguments and pushes them into a specific array.
I have managed to do this with the following code:
var array=[];
function append(){
for(var i=0;i<arguments.length;i++)
array.push(arguments[i]);
}
Now my question is:Can I rewrite the append function without using "for loop"?
Thanks in advance.
If you need to get arguments array, you should use Array's slice function on an arguments object, and it will convert it into a standard JavaScript array:
var array = Array.prototype.slice.call(arguments);
You could use Array.prototype.push.apply
function append(){
// make arguments an array
var args = Array.prototype.slice.call(arguments);
// return the number of elements pushed in the array
return Array.prototype.push.apply(array, args);
}
So, what's happening here with args? We use Array.prototype.slice.call with arguments, the purpose being to make arguments an array, because it is a special object. Function.prototype.call is used to call a function with a specific context (aka this), and then the arguments to call the function with (comma separated). Conveniently, it appears that slice() looks at the length property of the this context, and arguments has one too, and when not empty, has properties from 0 to length -1, which allows slice to copy arguments in a new array.
You can rewrite this without a for loop, but you have to use a loop of some sort (you're working with multiple items, it's a necessity).
If you have access to ES6 or Babel, I would use something like:
function append(...args) {
return array.concat(args);
}
Without ES6, you need to work around the fact that arguments isn't a real array. You can still apply most of the array methods to it, by accessing them through the Array prototype. Converting arguments into an array is easy enough, then you can concat the two:
function append() {
var args = Array.prototype.map.call(arguments, function (it) {
return it;
});
return array.concat(args);
}
Bear in mind that neither of these will modify the global array, but will return a new array with the combined values that can be used on its own or assigned back to array. This is somewhat easier and more robust than trying to work with push, if you're willing to array = append(...).
Actually i honestly believe that push must be redefined for the functional JS since it's returning value is the length of the resulting array and it's most of the time useless. Such as when it's needed to push a value and pass an array as a parameter to a function you cant do it inline and things get messy. Instead i would like it to return a reference to the array it's called upon or even a new array from where i can get the length information anyway. My new push proposal would be as follows;
Array.prototype.push = function(...args) {
return args.reduce(function(p,c) {
p[p.length] = c;
return p
}, this)
};
It returns a perfect reference to the array it's called upon.
I have found a behavior I did not expect when trying to use a loop in order to change the value set for a property in an object.
Basically, I declare my object outside the loop.
Then I loop on an array of numeric values, which values are used to update the object property.
Inside the loop, I store the current object state inside an external array.
The result is that instead of having an array containing a series of objects with different numeric values, I end up having the same numeric values in each object stored.
Here is the fiddle http://jsfiddle.net/fAypL/1/
jQuery(function(){
var object_container = [];
var numeric_values = [1, 2 , 3, 4];
var my_object = {};
jQuery.each(numeric_values, function(index, value){
my_object['value'] = value;
object_container.push(my_object);
});
jQuery.each(object_container, function(index, value){
jQuery('#content').prepend(value['value']);
});
});
I would expect to get 1 2 3 4 as values stored in each object, however, what I get is 4 4 4 4, which does not make sense to me.
Any hint on this behavior is more than welcome, thanks
When your code calls .push() and passes my_object, what's being passed is a reference to the object. No copy is made.
Thus, you've pushed four references to the exact same object into the array.
JavaScript objects always participate in expressions in the form of references. There's no other way to deal with objects. Thus when you create a variable, and set its value to be an object, you're really setting its value to be a reference to the object. Same with parameter passing, and anywhere else an object can appear in an expression.
In this case, you can create new objects pretty easily; just dispense with my_object and push a fresh one on each iteration:
object_container.push( { value: value } );
You are not creating a new object each time around the loop - you are just updating the same existing object and pushing references of that to the object array. To create a new object you want to do something like:
my_object = { 'value': value };
object_container.push(my_object);
In this case you now will get something more like what you were looking for. See the updated fiddle here: http://jsfiddle.net/fAypL/2/.
Best of luck!
One more thought (Clone!) - If you are really tied to using the same object each time, just clone the object before you add to the array. There is a great solution for that here.
You are using jQuery so if what you want is to merge without effecting the original look at :
var both_obj = $.extend( {}, default_obj , adding_obj );
This will leave your original object changed, also good to use for a copy.
jquery docs - extend()
An alternate version is to use an object with a constructor and the new keyword:
var object_container = [];
var numeric_values = [1, 2 , 3, 4];
function MyObject(value)
{
this.value = value;
}
jQuery.each(numeric_values, function(index, value){
object_container.push(new MyObject(value));
});
jQuery.each(object_container, function(index, value){
jQuery('#content').prepend(value['value']);
});
Fiddle
I was trying to define an array (including other arrays as values) in a single javascript statement, that I can loop through to validate a form on submission.
The function I wrote to (try to) create inline arrays follows:
function arr(){
var inc;
var tempa = new Array(Math.round(arguments.length/2));
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
This is called three times here to assign an array:
window.validArr = arr(
'f-county',arr('maxlen',10, 'minlen',1),
'f-postcode',arr('maxlen',8, 'minlen',6)
);
However in the javascript debugger the variable is empty, and the arr() function is not returning anything. Does anyone know why my expectations on what this code should do are incorrect?
(I have worked out how to create the array without this function, but I'm curious why this code doesn't work (I thought I understood javascript better than this).)
Well from what your code does, you're not really making arrays. In JavaScript, the thing that makes arrays special is the management of the numerically indexed properties. Otherwise they're just objects, so they can have other properties too, but if you're not using arrays as arrays you might as well just use objects:
function arr(){
var inc;
var tempa = {};
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
What you're seeing from the debugger is the result of it attempting to show you your array as a real array should be shown: that is, its numerically indexed properties. If you call your "arr()" function as is and then look at (from your example) the "f-county" property of the result, you'll see something there.
Also, if you do find yourself wanting a real array, there's absolutely no point in initializing them to a particular size. Just create a new array with []:
var tempa = [];
Your code works. Just inspect your variable, and you will see that the array has the custom keys on it. If not expanded, your debugger shows you just the (numerical) indixed values in short syntax - none for you.
But, you may need to understand the difference between Arrays and Objects. An Object is just key-value-pairs (you could call it a "map"), and its prototype. An Array is a special type of object. It has special prototype methods, a length functionality and a different approach: to store index-value-pairs (even though indexes are still keys). So, you shouldn't use an Array as an associative array.
Therefore, their literal syntax differs:
var array = ["indexed with key 0", "indexed with key 1", ...];
var object = {"custom":"keyed as 'custom'", "another":"string", ...};
// but you still can add keys to array objects:
array.custom = "keyed as 'custom'";