I am working on a project where I need to contain a lot of functions within an object so that I can access them with variables that combine in different ways to access different ones. I haven't written this in my main project yet, just tested it as a method and I'll put that test code below to hopefully better explain what I'm talking about. There will be a lot of functions in this object, and the aim is to have one of them randomly run when a button is pressed.
A key part of what I'm trying to make is that the user can exclude whichever functions they want from being randomly selected, and this is what is causing me some problems. I read through a lot of solutions for other problems, but all of them are just a little off (or at least I don't know how to apply them to this just yet). The first thing I tried was using an array and, depending on what option the user ticks, excluding or adding items to it and then randomly selecting from within that to get the random number, but I'm worried that an array of around 300 items would just slow things down or cause more problems than is really useful so I stepped away from this. I looked up how I could exclude numbers from the random generation too, but this tended to use for loops and only worked for a few numbers being excluded - after that I imagine that would cause problems too. I looked into excluding ranges of numbers from random generation as well, but the items that users exclude aren't guaranteed to be next to each other in the object, so that causes problems.
The plan in my head was to add a number at the end of the object key; the random number generator would choose from all non-excluded numbers (exclusion based on other parts of the key, such as excluding all 'salad' options in the test below), and then that number, along with the two or three other variables that make up the key bring us to an actual thing. So, in the theme of the below, "bacon" + "burger" + "lettuce" + "17" might be chosen based on the options the user chooses and the random number respectively and might lead to the 17th baconburgerlettuce based alert - I'm not making some kind of food shouting app, by the way, this really is just a demo...
This is... Hopefully less complicated than it sounds. I imagine I have explained it awfully, so if I can even just clarify something please let me know. Alternatively, if nesting functions inside an object for this purpose is straight up dumb, tell me! I want to learn the best way of doing things, rather than fumble through something ugly and inelegant!
var food;
var type;
var func = {
cheeseburger: function() {alert("This is CHEESE BURGER");},
baconburger: function() {alert("BACON BURGER reigns supreme!");},
cheesesalad: function() {alert("CHEESE SALAD hahahaha");},
baconsalad: function() {alert("BACON SALAD! That's right!");}
};
$(".cheese").click(function() {
food = "cheese";
$(".test").html(food);
});
$(".bacon").click(function() {
food = "bacon";
$(".test").html(food);
});
$(".burger").click(function() {
type = "burger";
$(".test2").html(type);
});
$(".salad").click(function() {
type = "salad";
$(".test2").html(type);
});
$(".go").click(function() {
func [food + type]();
});
const funcs = [
v => alert(v),
v => console.log(v),
v => alert( v + 1)
];
const excluded = new Set();
//to exclude a function
excluded.add(2);
function random(...args){
do {
var i = Math.floor( Math.random() * funcs.length );
} while( excluded.has( i ) );
funcs[i](...args);
}
//test it
random("test");
First of all you need some property to enable or disable functions.
var func = {
cheeseburger: {f: function() {...}, enabled: true},
baconburger: {f: function() {...}, enabled: true},
cheesesalad: {f: function() {...}, enabled: true},
baconsalad: {f: function() {...}, enabled: true},
...
};
Then a function to pick random one ignoring disabled ones.
Here arises first problem: If you just randomly pick one until you find one
which is not disabled, as the amount of disabled ones grows, the it will take
longer to find enabled one.
The easier solution is to use a cache like:
var cache;
function updateCache(){ // Using function we can update it when needed.
cache = Object.keys(func).filter(k=>func[k].enabled);
};
updateCache();
Then you need enable / disable methods too:
function enable(fn) {
func[fn].enabled = true;
updateCache();
};
function disable(fn) {
func[fn].enabled = false;
updateCache();
};
And, finally you just need to pick function keys from the cache:
function callRandomFn(){
var k = Math.floor(Math.random() * cache.length);
return fn[cache[k]]();
};
Related
and thank you for your patience. I am an experienced coder using old school methods, and have used the old style of Javascript for many years. I've known about JQuery, and such as that for a while, but have been "too busy" to learn the new things. Well, I am now working on learning those things, and have chosen AngularJS.
I am watching the tutorial videos, and reading the documents and API reference for AngularJS, but the different nomenclature does impede my progress. But I'm trying. For me, using an example of a "real" sort of problem helps me to understand how to leverage the functionality.
In this example, I have an array of Power Ball numbers, and want to be able to paste in the space delimited winning numbers you might copy off of the Power Ball site. This would then parse the input and hi-lite all of the individual numbers. Eventually, perhaps hi-liting any winning combinations in different ways. No, this isn't a product I'm building, but I thought this would cause me to use many different tools in this tool box.
So my questions are:
I initialized the array in my service (factory) and have shown that I can push to it from my controller. Is this correct?
Is my current way of doing things (e.g. ng-class usage, etc.) at least feasible, if not correct?
How could I use space delimited input in the input/text box to do the comparison for the style changes?
Here is an example of my JavaScript code. (note: I'm only pasting in one set of numbers for the array.)
var lotteryCheckApp = angular.module('lotteryCheckApp', []);
lotteryCheckApp.factory('PowerBall', function() {
var PowerBall = {};
PowerBall.numArray = ["42"];
PowerBall.myNumbers = [
{
First_Number: '03',
Second_Number: '07',
Third_Number: '17',
Fourth_Number: '21',
Fifth_Number: '42',
PowerBall: '21'
}
];
return PowerBall;
});
lotteryCheckApp.controller('PowerBallNumbersCtrl',function($scope,PowerBall) {
$scope.powerball = PowerBall;
$scope.winningStyle = true;
$scope.powerball.numArray.push("21");
$scope.myCheck = function(searchNum,numVal) {
// the following code works for a simple input to value check
if(searchNum == numVal) return true;
else return false;
}
I have a JSFiddle link: http://jsfiddle.net/Divermarv/VpyxZ/1/
Again, thank you all for your patience and responses.
Glad to hear you're getting into angular. Here is how I would solve your problem JSFiddle
I use ng-change to watch the input values,
<input type="text" ng-model="input" ng-change="convert()">
and feed that to a method that converts the input to an array of values.
$scope.convert = function() {
$scope.hiLite = $scope.input.split(" ");
}
Then your check function checks to see if the value is in the array
$scope.myCheck = function(searchNum,numVal) {
if(searchNum.indexOf(numVal) !== -1) return true;
else return false;
}
Is that kind of what you were thinking?
Well my short and easy to explain explanation can be this. I have 2 arrays, FilterList and GamesReset. Whenever this function I have works and filters out some games with check boxes and a drop down menu, the function starts off with something like FilterList=GamesReset;. This functions seems to work fine until I filter out ages for the game. The function never touches GamesReset unless it's something like while(i<GamesReset.length){} or FilterList=GamesReset;. And the only tool I use when I filter games is FilterList.splice(i,1);. Now with that, GamesReset definitely, should never change as far as I know. I have it to reset FilterList, then depending on what needs to be filtered out, it will start removing those games from the FilterList. The problem I have, is that, GamesReset also becomes filtered. Which, does not make any sense at all. So like my title, it's just like saying b=0;, a=b;, a++;, and now b equals 1.
Now, I think that's the best/shortest way I can reveal this problem, without overdoing it with my bad habit of explaining things to people. I have a webpage currently available if anyone would like to see whats going on in action, because I wouldn't get what's going on with GamesReset either if I were you, here (url removed, read edit). To get the error working, just change the age to 10 without checking any boxes. The bottom paragraph is the GamesReset array (using <br> to separate each array), and it's the one that changes when I'm only changing FilterList in the JavaScript. The actual codes if you view the page source may be a little off compared to when I mentioned above, but it's pretty much 100% the same thing. I also wanted to have the codes available without a url and on this page, but I can't figure out how to do that with the html tags included.
Actually, here's the JavaScript function. I just figured out the 4 spaces thing when my question was rejected.
function SearchFilter() {
Games = GamesReset;
plat = document.getElementById('platformcheck').checked;
rpg = document.getElementById('rpgcheck').checked;
puzz = document.getElementById('puzzlecheck').checked;
hybo = document.getElementById('hybocollectcheck').checked;
ages = document.getElementById('agescheck').value;
if ((!plat) && (!rpg) && (!puzz) && (!hybo)) {
FilterList = Games;
} else {
FilterList = [];
i = 0;
while (i < Games.length) {
Set = '';
Set = Games[i];
Set = Set.split('</>');
StrFind = Set[0];
if (
(plat && (StrFind.search(',platform,') > -1)) || (rpg && (StrFind.search(',rpg,') > -1)) || (puzz && (StrFind.search(',puzzle,') > -1)) || (hybo && (StrFind.search(',hybocollect,') > -1))) {
FilterList.push(Games[i]);
}
i++;
}
// so by now, we should have the filtered array
}
//seperate filter for ages
i = 0;
while (i < FilterList.length) { //The problem should definitely start here
Set = '';
Set = FilterList[i];
Set = Set.split('</>');
StrFind = Set[1];
if ((Math.abs(StrFind)) > ages) {
FilterList.splice(i, 1);
} else {
i++;
}
}
GL.innerHTML = GamesReset.join('<br>');
}
As a reminder, the problem starts when the age filter is working. And the only thing it does is FilterList.splice(i,1);. But it ends up changing GamesReset. I changed this function a bit when I added Games=GamesReset;, but that was another test to try and make sure GamesReset doesn't get filtered like FilterList, but it still does.
EDIT: I removed my url since the answers definitely explained everything, so there's no need for it now.
Arrays are not copied when assigned, both variables will refer to the same data. Here is a post that goes into detail on this: Copying array by value in JavaScript
It makes perfect sense since variables are just references to objects in memory. One object can have several references. Consider this:
var a = { foo: 'bar' };
var b = a;
// b is now a reference to a and they both point to the same object
b.foo = 'doe';
alert( a.foo ); // alerts doe
The same goes for arrays. So when you do FilterList = GamesReset you are not copying the array - you are just assigning the same array to another variable. Any mutations or changes made to either reference will be reflected in all references.
To create a copy of an array you can use slice:
FilterList = GamesReset.slice();
Let's say I have this array:
[{name: "A", works: true}, {name: "B", works: true}, {name: "C", works: false}]
I want a function to select one element of this array, with, let's say 80% probability of getting a working element (works: true).
How could I do this elegantly? Would appreciate pseudo-code or code in any language.
(I can use underscore.js if needed, if using js)
In your situation first you are willing to toss a coin to decide if you are going to pick a working element or less. This can be done in JS with Math.random() which returns a value in [0,1].
value = Math.random() <= 0.8
will set value to true 80% of the times. At this point you already know which kind of element you want so you can just pick a random one and check if works or not. If it's of the correct type return it, otherwise pick another random one.
If your list is pretty long this may require many picks, in this case you could split the list and keep two lists (working ones and not workings ones).
The Function:
function chooser(an_array) {
var node_found = false;
var works = Math.random() <= .80;
while(node_found === false) {
var node = an_array[Math.floor(Math.random()*an_array.length)];
if(node.works === works) node_found = true;
}
return node;
}
Edit: Decided I should test it. Runs through the above function 10,000 times, results are good: http://jsfiddle.net/YyZfA/4/
I have a for loop that cycles through the number of elements that the user has created. There are a lot of available settings in this plugin, and each element can receive it's specific settings.
User settings are entered in the following format: speed_x: "1000,500 > 1000,200 > 0,0"
This controls the speed_x in/out for 3 separate elements. The > divides by object and the commas separate the in/out.
So I can grab specific object speed_x values, I've split speed_x into speed_x_set (splitting by >) resulting in:
1 1000,500
2 1000,200
3 0,0`
3 Inside the loop, I grab the value by index (since it's the object #) and split it by comma (to get speed_x_in and speed_x_out.)
for(var i=0; i<OS.numberofobjects; ++i){
OS.speed_x_on_set[i]=speed_x_set[i].split(",")[0],
OS.speed_x_off_set[i]=speed_x_set[i].split(",")[1],
...
};
Everything is assigned by object and by setting in/out correctly into the master OS settings object. T*he problem is I have many, many settings which need to be split in this fashion...* for example: delay_x_set, speed_y_set, opacity_set, etc. Their names are all based on the default setting name, with "_set" added as shown above. Hopefully this provides enough information. Thanks!
I would avoid to access to the same item twice and perform the same split twice for each iteration. So, you could have something like:
for (var i = 0, item; item = speed_x_set[i++];) {
var values = item.split(",");
OS.speed_x_on_set.push(values[0]);
OS.speed_x_off_set.push(values[1]);
}
Notice that in JavaScript 1.7 (Firefox) you can simply have:
for (var i = 0, item; item = speed_x_set[i++];) {
var [on, off] = item.split(",");
OS.speed_x_on_set.push(on);
OS.speed_x_off_set.push(off);
}
And hopefully in the next version of ECMAScript as well.
It's called "destructuring assignment".
I would say to cache the split result
for(var objindex=0; objindex<OS.numberofobjects; ++objindex){
var splits = speed_x_set[objindex].split(","); //Cache the split so its does not need to be done twice
OS.speed_x_on_set[objindex] = splits[0];
OS.speed_x_off_set[objindex] = splits[1];
...
};
What you're looking for is called parallel assignment, but unfortunately, JavaScript doesn't have it.
In ruby, however, it is common to see similar patterns:
first, second = "first second".split
As others have noted, the obvious way would be to cache split results and assign them separately. Sorry for not answering your question directly.
ok. I have a dropdown. well call it dropdownA. I have several arrays already fashioned, that i want to loop through, and put each individual value out onto the page into it's own input field, based on the selection in the dropdown. I know how to put together the loop to get the values out onto the page. anyway, here is the meta code.
meta1array=new Array('','','','','','');
meta2array=new Array('','','','','','');
meta3array=new Array('','','','','','');
function metacode(id){
{
var a=get.the.dd.selIndex;
var b=get.the.dd.options[a].value; //comes back as 'meta1'. other opts are meta2, meta3
var c=b+'array';
for(i=0;i<c.count;i++)
{
loop here to popout values
}
}
i have looked everywhere, and i haven't come up with anything. I also admit that my brain is starting to mushify from a few weeks straight of coding, and this is the first time i have come across this. so, please. i would be greatful for any help.
Global variables are members of the window object.
var c = window[b + 'array'];
It might be wise to make your arrays members of some other object though, for tighter scoping (avoid cluttering the global namespace).
metaobject = {
meta1array: ['','','','',''],
meta2array: ['','','','',''],
meta3array: ['','','','','']
};
// snip
var arr = metaobject[b + 'array'];
for(var i=0;i<arr.length;i++) {
//do work
}
Check out the fiddle using jquery here.