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.
Related
So I have an interesting issue I am not sure how to follow, I need to use lodash to search two arrays in an object, looking to see if x already exists, lets look at a console out put:
There are two keys I am interested in: questChains and singleQuests, I want to write two seperate functions using lodash to say: find me id x in the array of objects where questChains questChainID is equal to x.
The second function would say: Find me a quest in the array of objects where singleQuests questTitle equals y
So if we give an example, you can see that questChainId is a 1 so if I pass in a 1 to said function I would get true back, I don't actually care about the object its self, else I would get false.
The same goes for singleQuests, If I pass in hello (case insensitive) I would get back true because there is a quest with the questTitle of 'Hello'. Again I don't care about the object coming back.
The way I would write this is something like:
_.find(theArray, function(questObject){
_.find(questObject.questChains, function(questChain){
if (questChain.questChainId === 1) {
return true;
}
});
});
This is just for the quest chain id comparison. This seems super messy, why? Because I am nesting lodash find, I am also nesting if. It gets a bit difficult to read.
Is this the only way to do this? or is there a better way?
Yeah it can be expressed more simply.
Try something like this:
var exampleArray = [{
questChains: [{
questChainId: 1,
name: 'foo'
}, {
questChainId: 2,
name: 'bar'
}],
singleQuests: [{
questTitle: 'hello world'
}]
}, {
questChains: [{
questChainId: 77,
name: 'kappa'
}]
}];
var result = _.chain(exampleArray)
.pluck('questChains')
.flatten()
.findWhere({ questChainId: 2 })
.value();
console.log('result', result);
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
Using chain and value is optional. They just let you chain together multiple lodash methods more succinctly.
pluck grabs a property from each object in an array and returns a new array of those properties.
flatten takes a nested array structure and flattens it into flat array structure.
findWhere will return the first element which matches the property name/value provided.
Combining all of these results in us fetching all questChain arrays from exampleArray, flattening them into a single array which can be more easily iterated upon, and then performing a search for the desired value.
Case-insensitive matching will be slightly more challenging. You'd either need to either replace findWhere with a method which accepts a matching function (i.e. find) or sanitize your input ahead of time. Either way you're going to need to call toLower, toUpper, or some variant on your names to standardize your search.
I have an array of objects which are presented to the user as blocks. They can drag and drop to change the order that that blocks appear, which I then want to change the position of the objects in the array.
$scope.myArray = [{a:1,b:2,c:3}, {a:11,b:22,c:33}, {a:111,b:222,c:333}];
function orderChanged(event) {
console.log($scope.myArray);
//logs [{a:1,b:2,c:3}, {a:11,b:22,c:33}, {a:111,b:222,c:333}]
console.log("source:", event.source.index, "dest:", event.dest.index);
//logs source: 1 dest: 2
$scope.myArray.move(event.source.index, event.dest.index);
console.log($scope.myArray);
//logs [{a:1,b:2,c:3}, {a:11,b:22,c:33}, {a:111,b:222,c:333}]
};
//this is going to rearrange the array
Array.prototype.move = function (from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
The orderChange event has the source index and destination index as integers that represent their order as present to the user, which also maps to their positions in the array before any moving has occurred.
I cannot get the array to rearrange, each time I log the array in the orderChange function both logs return the same order.
All of the other examples of array re-ordering are for arrays that do not contain objects, I'm wondering if this is what is messing up my code?
I'm testing your code and works ok. Where are you modifying the Array prototype? Just try to replace the call to move() with the code that actually does the reorder and test...
Anyway, you're using AngularJS. Why are you messing with the DOM? Add a property called Order to each of your objects and let Angular do the syncrhonisation... that what is meant for.
In short, take a look at this module, maybe it would do your life easier:
http://ngmodules.org/modules/ng-sortable
I think, your code works ok, but the log does not.
console.log might not represent the values at runtime, but at viewtime, especially with multidimensional objects and arrays.
Try a different log to see console.log($scope[0].a, $scope[1].a, $scope[2].a)
You might want to check in a different brwoser, as this seems to be a Chrome issue, see here:
Is Chrome's JavaScript console lazy about evaluating arrays?
Wrong value in console.log
I am learning JavaScript based on Eloquent Javascript and during one of the chapters, came across this error. Not sure what I am doing wrong here. I am getting an error "Cannot read property 'indexOf' of undefined" against the code return journal.events.indexOf(event) != -1
Also, can someone explain how that line works? Isn't IndexOf supposed to return the first position of occurrence of the specified value (in this case, event)? But I see in the book that the line return journal.events.indexOf(event) != -1; returns either true or false.
var journal = [];
function addEntry(events, didITurnIntoASquirrel) {
journal.push({
events: events,
squirrel: didITurnIntoASquirrel
});
}
addEntry(["work", "touched tree", "pizza", "running",
"television"], false);
addEntry(["work", "ice cream", "cauliflower", "lasagna",
"touched tree", "brushed teeth"], false);
addEntry(["weekend", "cycling", "break", "peanuts",
"beer"], true);
function hasEvent(event, entry) {
return entry.events.indexOf(event) != -1;
}
console.log(hasEvent("pizza", journal));
In your sample code journal is an array
var journal = []; <--- Array
Therefore events should be accessed with an index like:
journal[0].events.indexOf(event)
^
|
|
Here you need to find the right index to get your events
I'm unsure how far along you are with learning javascript, so forgive me if some of this sounds condescending or obvious.
Let's break this down one step at a time. You begin with an empty array.
var journal = [];
console.log(journal); //[]
//it's defined. It's an empty Array.
By calling push on an array, you add something to the end of the array.
More on Array.push.
I don't like this example for beginners because it expects you to know already that you can define an object while you're passing it as an argument. This is done this way because you don't truly need a variable reference to an object that's only used once and is therefore a good way of reducing bloat in code. But verbosity is much better when teaching someone, imho.
//Wait, what am I pushing into the journal array?
journal.push({
events: events,
squirrel: didITurnIntoASquirrel
});
This should make more sense:
Create an object first. Then add that object to the "journal" array.
function addEntry(events, didITurnIntoASquirrel) {
var temporaryObject = {
events: events,
squirrel: didITurnIntoASquirrel
};
journal.push(temporaryObject);
}
Now journal is an array with an unnamed object at its first index.
1. console.log(journal); // [temporaryObject]
2. console.log(journal[0]); - //temporaryObject
The visibile difference is the lack of parens, but the difference is important.
On line 1 you have the array itself, on line 2 you have what's inside it (i.e. the object). You need to get the object (via the technique on line 2) before you can access properties of that object, such as "events" or "squirrel". Moving on.
addEntry(["work", "touched tree", "pizza", "running", "television"], false);
Next, we invoke the addEntry function. Same confusion here. I've rewritten it slightly to make the arguments more understandable.
var entry = ["work", "touched tree", "pizza", "running", "television"];
addEntry(entry, false);
//repeat 2 more times with different data
So first we define an array, then we pass it to the addEntry function. when the addEntry function runs (it will run right when we invoke it), the "entry" argument will be represented function as the "events" parameter (simple way: events = entry and didITurnIntoASquirrel = false). some notes on parameters vs arguments.
So you should be able to understand now that you're passing an array and a boolean to the addEntry function. That function creates an object based on those values referencing them via their parameters. That object is then added to the journal array.
What you end up with is 4 levels of depth. You have an array called journal, which has objects in it. Those objects have a property called events, which is a different array. That array has several strings inside it. To access the events array and use indexOf to see if it has a given string in it, you need to traverse that depth one level at a time.
//journal is the array, journal[0] is the object, journal[0].events is the property of that object
console.log(journal[0].events) //["work", "touched tree", "pizza", "running", "television"].
Note this is the same data that we originally put in the entry variable. This may seem unnecessarily complicated, but trust me when I say this type of structure is useful in real life when you need to manage data hierarchy or other logical relationships between "things" in Object Oriented programming.
Now, all the work we've done so far is to add to the journal array. We now want a function to see what's inside it. Why a function? So you don't have to rewrite the same code over and over.
function hasEvent(event, journal) {
return journal.events.indexOf(event) != -1;
}
By now I hope you can spot the error in this function. journal.events doesn't work, because journal is an array, not an object (you skipped a level, and your computer isn't smart enough to know what you mean) journal[0].events would work, because you are telling javascript ("from the journal array, I want the object in the first slot, and the events property of that object").
The simplest fix is to send journal[0] to the hasEvent function instead of journal. Beware, this will only check journals first index. Realistically you'd want a for loop inside the hasEvent function or wrapping the call to that function to check all indexes. For now we will hardcode them, since we know there are 3, but its not a good idea in real life, since later there may be more than 3 entries in the journal).
This funciton is returning the result of calling indexOf() (some number or -1) with -1. Let's again rewrite it so that it makes more sense.
New hasEvent function:
//I renamed the variable so it makes more sense what it really is. It's the object, not the journal array.
function hasEvent(event, journalEntry) {
var index = journalEntry.events.indexOf(event);
var result = (index != -1); //true if it was found, false if it wasn't found.
return result; //a boolean based on the above comparison.
}
//Ack! My kingdom for a "for loop". Don't worry about that right now.
console.log(hasEvent("pizza", journal[0]));
console.log(hasEvent("pizza", journal[1]));
console.log(hasEvent("pizza", journal[2]));
TL;DR
Here is a fiddle with working code:
http://jsfiddle.net/o8dg1ts6/1/
To answer your 2nd question:
"Isn't IndexOf supposed to return the first position of occurrence of the specified value"
Yes, and indexOf returns -1 if if the value is not found in the array.
So if the event is found, then the expression indexOf(event) != -1 will evaluate to true.
I'm calling a JavaScript function that wants an array of things to display. It displays a count, and displays the items one by one. Everything works when I pass it a normal JavaScript array.
But I have too many items to hold in memory at once. What I'd like to do, is pass it an object with the same interface as an array, and have my method(s) be called when the function tries to access the data. And in fact, if I pass the following:
var featureArray = {length: count, 0: func(0)};
then the count is displayed, and the first item is correctly displayed. But I don't want to assign all the entries, or I'll run out of memory. And the function currently crashes when the user tries to display the second item. I want to know when item 1 is accessed, and return func(1) for item 1, and func(2) for item 2, etc. (i.e., delaying the creation of the item until it is requested).
Is this possible in JavaScript?
If I understand correctly, this would help:
var object = {length: count, data: function (whatever) {
// create your item
}};
Then, instead of doing array[1], array[2], et cetera, you'd do object.data(1), object.data(2), and so on.
Since there seems to be a constraint that the data must be accessed using array indexing via normal array indexing arr[index] and that can't be changed, then the answer is that NO, you can't override array indexing in Javascript to change how it works and make some sort of virtual array that only fetches data upon demand. It was proposed for ECMAScript 4 and rejected as a feature.
See these two other posts for other discussion/confirmation:
How would you overload the [] operator in Javascript
In javascript, can I override the brackets to access characters in a string?
The usual way to solve this problem would be to switch to using a method such as .get(n) to request the data and then the implementor of .get() can virtualize however much they want.
P.S. Others indicate that you could use a Proxy object for this in Firefox (not supported in other browsers as far as I know), but I'm not personally familiar with Proxy objects as it's use seems rather limited to code that only targets Firefox right now.
Yes, generating items on the go is possible. You will want to have a look at Lazy.js, a library for producing lazily computed/loaded sequences.
However, you will need to change your function that accepts this sequence, it will need to be consumed differently than a plain array.
If you really need to fake an array interface, you'd use Proxies. Unfortunately, it is only a harmony draft and currently only supported in Firefox' Javascript 1.8.5.
Assuming that the array is only accessed in an iteration, i.e. starting with index 0, you might be able to do some crazy things with getters:
var featureArray = (function(func) {
var arr = {length: 0};
function makeGetter(i) {
arr.length = i+1;
Object.defineProperty(arr, i, {
get: function() {
var val = func(i);
Object.defineProperty(arr, i, {value:val});
makeGetter(i+1);
return val;
},
configurable: true,
enumerable: true
});
}
makeGetter(0);
return arr;
}(func));
However, I'd recommend to avoid that and rather switch the library that is expecting the array. This solution is very errorprone if anything else is done with the "array" but accessing its indices in order.
Thank you to everyone who has commented and answered my original question - it seems that this is not (currently) supported by JavaScript.
I was able to get around this limitation, and still do what I wanted. It uses an aspect of the program that I did not mention in my original question (I was trying to simplify the question), so it is understandable that other's couldn't recommend this. That is, it doesn't technically answer my original question, but I'm sharing it in case others find it useful.
It turns out that one member of the object in each array element is a callback function. That is (using the terminology from my original question), func(n) is returning an object, which contains a function in one member, which is called by the method being passed the data. Since this callback function knows the index it is associated with (at least, when being created by func(n)), it can add the next item in the array (or at least ensure that it is already there) when it is called. A more complicated solution might go a few ahead, and/or behind, and/or could cleanup items not near the current index to free memory. This all assumes that the items will be accessed consecutively (which is the case in my program).
E.g.,
1) Create a variable that will stay in scope (e.g., a global variable).
2) Call the function with an object like I gave as an example in my original question:
var featureArray = {length: count, 0: func(0)};
3) func() can be something like:
function func(r) {
return {
f : function() {featureArray[r + 1] = func(r + 1); DoOtherStuff(r); }
}
}
Assuming that f() is the member with the function that will be called by the external function.
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.