I've gotten a Random Lotto Generator script to work in JavaScript in one game. Now, I'm trying to get it to function for multiple Lotto games and figured Objects were the best way to adjust to the different formats of each game to account for different number pools, different selected numbers, 'bonus balls', duplicate numbers, etc.
const game = [{name: "Lotto Texas", balls: 6, numbers: 54, start: 1, duplicates: false, bonusBall: false, results: []},
{name: "Pick 3", balls: 3, numbers: 10, start: 0, duplicates: true, bonusBall: false, results: []},
{name: "Powerball", balls: 5, numbers: 69, start: 1, duplicates: false, bonusBall: true, bonusNumbers: 26, results: [], bonus: []}];
game.forEach (lottoResults()); // gets results
function lottoResults(){
this.results.length = this.balls; //Here is where the error occurs. This is supposed to assign the balls property to the results array's length value.
if (!this.duplicates){ //checks if duplicates allowed
this.results.forEach(duplicateCheck());
}
else{
this.results.forEach(lottoBall())
}
if(this.bonusBall){
this.bonus.push(lottoBall(this.bonusNumbers, this.start)); //assigns a number to bonus ball
}
}
I'm trying to set a blank array initialized in each object, with the amount with the 'balls' property (referring to lotto balls) setting how big the array is dependent on the game. However, anytime I try to set that up, I keep getting an Uncaught TypeError: Undefined is not a function. I've tried setting the object property value to a primitive variable, establishing the array length within the initialized object variables, but still get the same error in some form. Am I just misusing the object and should be calling the values differently?
You are invoking lottoResults here within forEach by placing () after the function name
game.forEach (lottoResults());
Instead, you should pass the function like this
game.forEach (lottoResults);
ref: https://www.w3schools.com/jsref/jsref_foreach.asp
Note: Same thing applies to duplicateCheck and lottoBall passed to forEach, remove the ()
The concept of this in JavaScript can be rather difficult to grasp. And occasionally, even experienced programmers may struggle with it.
As #Vin_it mentioned above, you're use of the this keyword is not referring to each item in the game array. If you execute the following code in your browser's console, you will notice that this is referring to the Window object. Don't worry if you don't get it straight away, but save this article and refer to it in the future when you face this issue.
//This is a reference to the Window Object:
function lottoResults(){
console.log(this)
}
lottoResults()
Instead of using "this", you can iterate over arrays easily using the following construction:
forEach((item) => {
//do somthing with the item:
duplicateCheck(item.results)
})
As shown above, for sake of clarity, you might move your extra forEach inside the duplicateCheck function. Just set it to receive an array as parameter and adjust your code accordingly. This way you can keep the all code involved in verifying duplicates isolated.
Also, when you specify the length of an array as you are doing in the code below, you are basically creating and empty array of a given length with undefined items. If this was undesired, check the length property documentation in MDN.
function lottoResults(){
this.results.length = this.balls; //here
//...
}
While this answer doesn't doesn't show exactly how to write your code, I hope it explains why you are getting "Uncaught TypeError: Undefined" when using this.
Keep up the good work.
I have an array containing information on playing cards (cardInfo). A sample of the array is added in the code below. Since each card may have duplicates, I want to use this information to create a deck by pushing its information to a new array (drawPile) for each card of that type (the property 'frequency1').
var common = 4;
var uncommon = 3;
var rare = 2;
var cardInfo = [
{name:'Card A',frequency1:common,frequency2:rare,frequency3:0},
{name:'Card B',frequency1:common,frequency2:uncommon,frequency3:0},
{name:'Card C',frequency1:uncommon,frequency2:uncommon,frequency3:0}
];
var drawPile = [];
for (var cType = 0; cType < cardInfo.length; cType++) {
for (var freq = 0; freq < cardInfo[cType].frequency1; freq++) {
drawPile.push(cardInfo[cType]);
console.log(drawPile.length - 1);
drawPile[(drawPile.length - 1)].id = (drawPile.length - 1);
console.log(drawPile[(drawPile.length - 1)]);
}
}
The resulting console log, however, shows that all 4 "Card A" cards have the id property 3, all 4 "Card B" cards have the id property 7, and all 3 "Card C" cards have the id property 10. It is as if the nested (freq) loop only runs for all .push() commands before it adds the id property.
More strangeness: when I run this code in jsfiddle, I can replicate these results if I first run it and then open the console log, but when I run it after the console lof is already open, it works as intended.
How do I ensure each card gets a unique identifier?
EDIT: It gets even stranger when I get the exact same results if I create a completely new for loop specifically for adding the id property as seen in this code.
As you loop around the cardInfo collection, for each cardInfo instance, you loop around the frequencies. Technically for each frequency you are pulling in the same instance of the cardInfo, so technically you are updating the same cardInfo instance for each frequency iteration. Now your next statement will be but I write console.log for each iteration. But when you debug the code, console.log outputs correctly, however I'm not sure whether this is the case when it is run through without a debug. So how can you resolve this?
After determining that it was modifying the same instance for each iteration of the related frequency, I decided to clone the card (I'm not saying this is a great idea, but it proves the concept). I achieved this by using:
JSON.parse(JSON.stringify(cardInfo[cType]));
Again this is not the most performant way of achieving this, but highlights where I believe the issue lies.
I have applied this change to a fiddle (I've also simplified the code a little) and if you remove the cloning it operates as you currently see it, but with the cloning it works as you would expect it.
I hope that helps
You are pushing object reference to the drawPile array in the loop. That's why all "Card A" cards have the same ID as array contains same reference. you have to clone the object before pushing into the loop. You can make the below change to your code.
for (var freq = 0; freq < cardInfo[cType].frequency1; freq++) {
drawPile.push(JSON.parse(JSON.stringify(cardInfo[cType])));
console.log(drawPile.length - 1);
drawPile[(drawPile.length - 1)].id = (drawPile.length - 1);
console.log(drawPile[(drawPile.length - 1)]);
}
When I try to console.log an object in Chrome, it states in the first line of the console.log (where you see Object { small summary here } that my posts array is of length 0 (posts: Array[0]).
However when I expand the post it shows that it has 27 items in it (which is what I need).
This happens to me randomly and I got no idea why it is happening, anybody experienced this before?
Screenshot:
Update: This happens in all browsers, so it is probably not chrome related
The debugger can't know if an object has been altered, this is why the posts attribute's rendering (in your example) has not been updated. Even if the debugger would be able to know when a attribute has been changed updating it every time (and all "loggings") would be too expensive.
So the debugger will check the attribute only when accessing it explicitly.
Chrome in this case will do this even only once:
p = []; window.x = {x: p};
Object {x: Array[0]}
x: Array[0]
__proto__: Object
x.x.push(1);
1
x.x.push(2);
2
Klicking x, the Array updates
p = []; window.x = {x: p};
Object {x: Array[2]}
x: Array[2]
0: 1
1: 2
length: 2
__proto__: Array[0]
__proto__: Object
Adding one item more to the array and toggleing x again, the size and entries remain
x.x.push(3)
3
x: Array[2]
0: 1
1: 2
length: 2
__proto__: Array[0]
In my opinion it's not necessary for the logger to update the object value since the variable watch has this function already. There you can always update the current value of a variable.
This works in Firebug and Chrome. Here's an example for Chrome:
The answer of "try-cache-finally" is correct too but I want to contribute the answer that caused the problem to happen in the first place.
when you have this code:
var b = [];
function a(callback) {
if (b.length > 0) {
return callback();
}
doXhr(function() {
b.push("something");
})
}
when you now call a twice then the second call will not see the updated a because of the XHR requests still being in process, this causes the xhr request to be called twice.
The solution for this is to wait on the callback for a like this:
a(function() {
a(function() {
// Correct trigger
});
});
So actually basic async programming, but this error was because of an already existing code base with this bug in it and it was hard to track down.
I have an array like:
errors = [ {...}, {...}, {...} ]
It's an instanceof array, yet it only returns 1 for .length?
Relevant code:
if(data.error){
errors.push({'element':ele,error:data.error});
}
//Above is looped a few times and there are N number of errors now inside
console.log(errors) //Returns 2+ objects like {...}, {...}
console.log(errors.length) //Returns 1
For Uzi and Muirbot, here's the errors array:
[
Object
element: b.fn.b.init[1]
error: "You must enter "example" into the field to pass"
__proto__: Object
,
Object
element: b.fn.b.init[1]
error: "Crap!"
__proto__: Object
It is correct, this code:
var errors = new Array();
errors.push({'element':'ele', error:'data.error'});
...adds ONE object to the array. The object has two properties.
It's possible your code is executing in an order other than what you're expecting. ie, when you log both errors and errors.length, errors does contain only 1 object. But after that you are adding to the errors array, and only after that are you looking at the console. At that point you could see a larger array in errors for two reasons - first, your actual code isn't logging errors but some object that contains errors. In that case the console display is live, and will show you not what was in errors at the time, but what is in it now. Alternatively, the console could just be taking some time to log errors.
Without more code I can't be sure if this is the case. But you could verify it by replacing console.log(errors); with console.log(errors[1]);. If errors is really only 1 long at the time, it will log undefined.
The problem was that Chrome's Web Inspector's console.log is an async event. So, the length was a property lookup so it gave that back instantly, but the object with two items inside was held off until the rest of the events had fired.
In the future I, and others with this issue, should use debugger; instead.
is it an Array object or something that resembles it?
arrays do work:
> a = [{a:1}, {b:2}]
[Object, Object]
> a.length
2
you'll have to provide more code.
and now that you've provided the relevant code, the correct answer is what Steve Wellens said (which was downvoted, by the way).
Array.push adds a single element, objects may have more than one key but they're still a single object so your real case was different from your original example, which of course works.
another possibility:
> a = []
[]
> a.length = 2
2
> a
[]
> a.length
2
> a instanceof Array
true
The only difference I see in map and foreach is that map is returning an array and forEach is not. However, I don't even understand the last line of the forEach method "func.call(scope, this[i], i, this);". For example, isn't "this" and "scope" referring to same object and isn't this[i] and i referring to the current value in the loop?
I noticed on another post someone said "Use forEach when you want to do something on the basis of each element of the list. You might be adding things to the page, for example. Essentially, it's great for when you want "side effects". I don't know what is meant by side effects.
Array.prototype.map = function(fnc) {
var a = new Array(this.length);
for (var i = 0; i < this.length; i++) {
a[i] = fnc(this[i]);
}
return a;
}
Array.prototype.forEach = function(func, scope) {
scope = scope || this;
for (var i = 0, l = this.length; i < l; i++) {
func.call(scope, this[i], i, this);
}
}
Finally, are there any real uses for these methods in JavaScript (since we aren't updating a database) other than to manipulate numbers like the following?
alert([1,2,3,4].map(function(x){ return x + 1})); // This is the only example I ever see of map in JavaScript.
The essential difference between map and forEach in your example is that forEach operates on the original array elements, whereas map explicitly returns a new array as a result.
With forEach you are taking some action with -- and optionally changing -- each element in the original array. The forEach method runs the function you provide for each element, but returns nothing (undefined). On the other hand, map walks through the array, applies a function to each element, and emits the result as a new array.
The "side effect" with forEach is that the original array is being changed. "No side effect" with map means that, in idiomatic usage, the original array elements are not changed; the new array is a one-to-one mapping of each element in the original array -- the mapping transform being your provided function.
The fact that there's no database involved does not mean that you won't have to operate on data structures, which, after all, is one of the essences of programming in any language. As for your last question, your array can contain not only numbers, but objects, strings, functions, etc.
The main difference between the two methods is conceptual and stylistic: You use forEach when you want to do something to or with each element of an array (doing "with" is what the post you cite meant by "side-effects", I think), whereas you use map when you want to copy and transform each element of an array (without changing the original).
Because both map and forEach call a function on each item in an array, and that function is user-defined, there is almost nothing you can do with one and not with the other. It's possible, though ugly, to use map to modify an array in-place and/or do something with array elements:
var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.map(function(el) {
el.val++; // modify element in-place
alert(el.val); // do something with each element
});
// a now contains [{ val: 2 }, { val: 3 }, { val: 4 }]
but much cleaner and more obvious as to your intent to use forEach:
var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.forEach(function(el) {
el.val++;
alert(el.val);
});
Especially if, as is usually the case in the real world, el is a usefully human-readable variable:
cats.forEach(function(cat) {
cat.meow(); // nicer than cats[x].meow()
});
In the same way, you can easily use forEach to make a new array:
var a = [1,2,3],
b = [];
a.forEach(function(el) {
b.push(el+1);
});
// b is now [2,3,4], a is unchanged
but it's cleaner to use map:
var a = [1,2,3],
b = a.map(function(el) {
return el+1;
});
Note as well that, because map makes a new array, it likely incurs at least some performance/memory hit when all you need is iteration, particularly for large arrays - see http://jsperf.com/map-foreach
As for why you'd want to use these functions, they're helpful any time you need to do array manipulation in JavaScript, which (even if we're just talking about JavaScript in a browser environment) is pretty often, almost any time you're accessing an array that you're not writing down by hand in your code. You might be dealing with an array of DOM elements on the page, or data pulled from an Ajax request, or data entered in a form by the user. One common example I run into is pulling data from an external API, where you might want to use map to transform the data into the format you want and then use forEach to iterate over your new array in order to display it to your user.
The voted answer (from Ken Redler) is misleading.
A side effect in computer science means that a property of a function/method alters a global state [Wikipedia]. In some narrow sense, this may also include reading from a global state, rather than from arguments. In imperative or OO programming, side effects appear most of the time. And you are probably making use of it without realizing.
The significant difference between forEach and map is that map allocates memory and stores the returning value, while forEach throws it away. See the ECMA specification for more information.
As for the reason why people say forEach is used when you want a side effect is that the return value of forEach is always undefined. If it has no side effect (does not change global state), then the function is just wasting CPU time. An optimizing compiler will eliminate this code block and replace the it with the final value (undefined).
By the way, it should be noted that JavaScript has no restriction on side effects. You can still modify the original array inside map.
var a = [1,2,3]; //original
var b = a.map( function(x,i){a[i] = 2*x; return x+1} );
console.log("modified=%j\nnew array=%j",a,b);
// output:
// modified=[2,4,6]
// new array=[2,3,4]
This is a beautiful question with an unexpected answer.
The following is based on the official description of Array.prototype.map().
There is nothing that forEach() can do that map() cannot. That is, map() is a strict super-set of forEach().
Although map() is usually used to create a new array, it may also be used to change the current array. The following example illustrates this:
var a = [0, 1, 2, 3, 4], mapped = null;
mapped = a.map(function (x) { a[x] = x*x*x; return x*x; });
console.log(mapped); // logs [0, 1, 4, 9, 16] As expected, these are squares.
console.log(a); // logs [0, 1, 8, 27, 64] These are cubes of the original array!!
In the above example, a was conveniently set such that a[i] === i for i < a.length. Even so, it demonstrates the power of map(), and in particular its ability to change the array on which it is called.
Note1:
The official description implies that map() may even change length the array on which it is called! However, I cannot see (a good) reason to do this.
Note 2:
While map() map is a super-set of forEach(), forEach() should still be used where one desires the change a given array. This makes your intentions clear.
You can use map as though it were forEach.
It will do more than it has to, however.
scope can be an arbitrary object; it's by no means necessarily this.
As for whether there are real uses for map and forEach, as well to ask if there are real uses for for or while loops.
While all the previous questions are correct, I would definitely make a different distinction. The use of map and forEach can imply intent.
I like to use map when I am simply transforming the existing data in some way (but want to make sure the original data is unchanged).
I like to use forEach when I am modifying the collection in place.
For instance,
var b = [{ val: 1 }, { val: 2 }, { val: 3 }];
var c = b.map(function(el) {
return { val: el.val + 1 }; // modify element in-place
});
console.log(b);
// [{ val: 1 }, { val: 2 }, { val: 3 }]
console.log(c);
// [{ val: 3 }, { val: 4 }, { val: 5 }]
My rule of thumb being making sure when you map you are always creating some new object/value to return for each element of the source list and returning it rather than just performing some operation on each element.
Unless you have any real need to modify the existing list, it doesn't really make sense to modify it in place and fits better into functional/immutable programming styles.
TL;DR answer --
map always returns another array.
forEach does not. It is up to you to decide what it does. Return an array if you want or do something else if you don't.
Flexibility is desirable is certain situations. If it isn't for what you are dealing with then use map.
Others have already posted about your main question regarding the difference between the functions. But for...
are there any real uses for these methods in JavaScript (since we aren't updating a database) other than to manipulate numbers like this:
...it's funny you should ask. Just today I wrote a piece of code that assigns a number of values from a regular expression to multiple variables using map for transformation.
It was used to convert a very complicated text-based structure into visualizable data ... but for simplicity's sake, I shall offer an example using date strings, because those are probably more familiar for everyone (though, if my problem had actually been with dates, instead of map I would've used Date-object, which would've done the job splendidly on its own).
const DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z$/;
const TEST_STRING = '2016-01-04T03:20:00.000Z';
var [
iYear,
iMonth,
iDay,
iHour,
iMinute,
iSecond,
iMillisecond
] = DATE_REGEXP
// We take our regular expression and...
.exec(TEST_STRING)
// ...execute it against our string (resulting in an array of matches)...
.slice(1)
// ...drop the 0th element from those (which is the "full string match")...
.map(value => parseInt(value, 10));
// ...and map the rest of the values to integers...
// ...which we now have as individual variables at our perusal
console.debug('RESULT =>', iYear, iMonth, iDay, iHour, iMinute, iSecond, iMillisecond);
So ... while this was just an example - and only did a very basic transformation for the data (just for sake of example) ... having done this without map would've been a much more tedious task.
Granted, it is written in a version of JavaScript that I don't think too many browsers support yet (at least fully), but - we're getting there. If I needed to run it in browser, I believe it would transpile nicely.