JS function to generate unique random number - javascript

I have the following utility function:
var Utilities = (function (array, maxN) {
function generateRandomNumber(array, maxN) {
let randomN = Math.floor(Math.random() * (maxN - 0) + 0);
if(array.length == maxN) {
array = [];
}
if(!array.includes(randomN)) {
array.push(randomN);
}
else {
randomN = generateRandomNumber(array, maxN);
}
return randomN;
}
return {
generateRandomNumber: generateRandomNumber
};
})();
export default Utilities;
which I am using like so:
function getRandomNumber() {
var array = [];
let randomN = Utilities.generateRandomNumber(array, 5);
return randomN;
}
I am then using this function inside a react component like so:
...
getNewQuestion = () => {
var randomN = getRandomNumber();
console.log(randomN);
this.setState({
question: this.state.data.data.questions[randomN]
})
this.setCatBgColor(this.state.data.data.questions[randomN].category);
}
...
I have just made a test and I got:
0,0,3
which is wrong as they should be unique until the length of 5 (in this case) is reached.

Your function
function getRandomNumber() {
var array = [];
let randomN = Utilities.generateRandomNumber(array, 5);
return randomN;
}
does create a new empty array on every call, not remembering anything. generateRandomNumber won't know about the previous invocations. Put the variable declaration and initialisation outside of the function:
var array = [];
function getRandomNumber() {
return Utilities.generateRandomNumber(array, 5);
}

Related

Reusing recurrent function in AngularJS

I have a function that keeps repeating itself in my controller.
It looks like this:
//FUNCTION 1
$scope.selectedage = [];
$scope.pushage = function (age) {
age.chosen = true;
$scope.selectedage.push(age);
console.log($scope.selectedage);
};
$scope.unpushage = function (age) {
age.chosen = false;
var index=$scope.selectedage.indexOf(age)
$scope.selectedage.splice(index,1);
console.log($scope.selectedage);
}
//FUNCTION 2
$scope.selectedgender = [];
$scope.pushgender = function (gender) {
gender.chosen = true;
$scope.selectedgender.push(gender);
console.log($scope.selectedgender);
};
$scope.unpushgender = function (gender) {
gender.chosen = false;
var index=$scope.selectedgender.indexOf(gender)
$scope.selectedgender.splice(index,1);
console.log($scope.selectedgender);
}
I have it like 8 times for 8 different arrays.
Is there any way to write it once and reuse it just changing some values?
You can make a generic function that accepts a value (container) where it needs to write the "value". Like:
$scope.push = function(container, value){
value.chosen = true;
container.push(value);
console.log(container);
}
$scope.unpush = function(container, value){
value.chosen = false;
var index = container.indexOf(value)
container.splice(index, 1);
console.log(container);
}
//sample
$scope.push($scope.selectedage, 10);
$scope.push($scope.selectedgender, "Male");
function togglePushStatusOfItem(item, itemKey, chosenStatus){
item.status = chosenStatus;
if(chosenStatus == true){
$scope[itemKey].push(item);
} else {
var index=$scope[itemKey].indexOf(item)
$scope[itemKey].splice(index,1);
}
console.log($scope[itemKey]);
}
togglePushStatusOfItem(user, 'selectedAge',true);
refactoring code to be reused in service
(function() {
'use strict';
angular
.module('myApp')
.factory('ItemFactory', ItemFactory);
ItemFactory.$inject = [];
/* #ngInject */
function ItemFactory() {
var service = {
toggleItemStatus: toggleItemStatus
};
return service;
////////////////
/*
itemContainer - equivalent to scope
item - item to replace or push
itemKey - itemKey
chosenStatus - true to push and false to remove
*/
function toggleItemStatus(itemContainer, item, itemKey, chosenStatus) {
item.status = chosenStatus;
if (chosenStatus == true) {
itemContainer[itemKey].push(item);
} else {
var index = $scope[itemKey].indexOf(item)
itemContainer[itemKey].splice(index, 1);
}
console.log(itemContainer[itemKey]);
}
}
})();
in your controller, you may use it like this
ItemFactory.toggleItemStatus($scope, item, 'selectedAge', true);// to push item
ItemFactory.toggleItemStatus($scope, item, 'selectedAge', false);// to remove item
The only difference I made is that I used the same function to push and unpush item. I hope this doesn't confuse you.

Use Javascript Object to Angular Service

I am trying to add functions to a JS Object which will be used as a singleton service.
angular
.module('app.steps')
.factory('createStepsService', createStepsService);
createStepsService.$inject = [];
/* #ngInject */
function createStepsService() {
var steps;
var service = {
newSteps: function (current_step, total_steps) {
if (!steps) {
return new Steps(current_step, total_steps);
}
}
};
return service;
function Steps(current_step, total_steps) {
this.c_step = current_step;
this.t_step = total_steps;
}
Steps.prototype = {
addSteps: function (num) {
this.c_step += num;
},
setLastStep: function () {
this.lastStep = this.c_step = this.t_step;
}
};
}
When I run this line from the controller, I am not able to access
addSteps / setLastStep methods.
vm.createStepsService = createStepsService.newSteps(1, 3);
Why I don't see these methods? Were they created?
Thanks.
Your steps.prototype code is never ran.
This is because it appears after the return.
Change the order of your code to this:
/* #ngInject */
function createStepsService() {
var steps;
function Steps(current_step, total_steps) {
this.c_step = current_step;
this.t_step = total_steps;
}
Steps.prototype = {
addSteps: function (num) {
this.c_step += num;
},
setLastStep: function () {
this.lastStep = this.c_step = this.t_step;
}
};
var service = {
newSteps: function (current_step, total_steps) {
if (!steps) {
return new Steps(current_step, total_steps);
}
}
};
return service;
}
The reason that you can have a function declared before a return is because of JavaScript variable and function hoisting.
Your problem is that you are creating Steps.prototype after a return statement, so it will never be read.
In AngularJS, services are singletons objects that are instantiated only once per app.
And the factory() method is a quick way to create and configure a service.
It provides the function's return value i.e. Need to create an object, add properties to it, then it will return that same object.
For ex:
angular
.module('myApp',[])
.factory("createStepService", function(){
var stepServiceObj = {};
var c_step = 0;
var t_steps = 0;
var last_step = 0;
stepServiceObj.setCurrentStep = function(current_step){
c_step = current_step;
console.log('c_step1: ',c_step);
};
stepServiceObj.getCurrentStep = function(){
return c_step;
};
stepServiceObj.setTotalStep = function(total_steps){
t_steps = total_steps;
};
stepServiceObj.getTotalStep = function(){
return t_steps;
};
stepServiceObj.setLastStep = function(){
last_step = c_step = t_step;
};
stepServiceObj.getLastStep = function(){
return last_step;
};
stepServiceObj.addSteps = function(num){
return c_step += num;
};
return stepServiceObj;
});

Random select array with remove items (jQuery)

I am trying to find a way to get values from an array of random way without repeating them, I found the following solution:
var letters = ["A", "B", "C"];
var getRandom = (function(array) {
var notGivenItems = array.map(function(el) {
return el;
});
var getIndex = function() {
return Math.floor(Math.random() * notGivenItems.length);
};
return function() {
if (notGivenItems.length === 0) {
return;
}
return notGivenItems.splice(getIndex(), 1)[0];
};
})(letters);
console.log(getRandom());
console.log(getRandom());
console.log(getRandom());
console.log(getRandom());
If I print the console.log() 4 times, at last, the array appears as undefined, and that's precisely what I need. However, I need (function () {... don't be fired automatically, because the value that comes via AJAX. So, should be something like:
function selec() {
var getRandom = (function(array) {
var notGivenItems = array.map(function(el) {
return el;
});
var getIndex = function() {
return Math.floor(Math.random() * notGivenItems.length);
};
return function() {
if (notGivenItems.length === 0) {
return;
}
return notGivenItems.splice(getIndex(), 1)[0];
};
})(letters);
return getRandom();
}
console.log(selec());
But then, the function continues printing values continuously, without return undefined.
I hope I have understood your problem.. if yes, was really easy. Check this fiddle
var letters = ["A", "B", "C"];
function getRandom(array){
var notGivenItems = array.map(function(el){
return el;
});
var getIndex=function(){
return Math.floor(Math.random() * notGivenItems.length)
};
if (notGivenItems.length === 0)
return;
return array.splice(getIndex(), 1)[0];
}
console.log(getRandom(letters));
console.log(getRandom(letters));
console.log(getRandom(letters));
console.log(getRandom(letters));
What's happening is that every time you call selec(), it is re-creating the getRandom function.
What you need to do is define getRandom outside of the selec() function scope.
You can do it at the same level:
var getRandom = (function(array) { ... })(letters);
var selec = function() { return getRandom; };
or you can create a closure to protect getRandom from naming conflicts:
var selec = (function() {
var getRandom = (function(array) { ... })(letters);
return function() { return getRandom; };
})();

Writing a function to set some but not necessarily all parameters in another function

I had a coding interview test that asked the following question which I was not able to fully solve. I'm wondering the best way to do this following my approach -- also sorry this is long.
You are given a function to read in like this (not necessarily 2 parameters):
function add(a, b) {
return a + b;
}
The objective is to create a function to initialize some of those variables and again call the function to perform the calculation like, function setParam(func, params). To use this you would do the following:
_add = setParam(add, {b:9})
_add(10) // should return 19
My solution was to parse the function to see how many parameters there are, then set them using the given parameters but since I barely know javascript I was never able to actually return a function with only some variables set and others still undefined.
(attempt at solution)
function setParam(func, params) {
// varray is an array of the the varriables from the function, func
// ie varray = [a,b] in this test
var varray = /function[^\(]*\(([^\)]*)\)/.exec(func.toString())[1].split(',');
//creates an array, paramset, that has the variables in func defined
//where possible
// ex paramset = [a,9] if only b was set
var paramsset = []
for (i = 0; i < varray.length; i++) {
if (typeof(params[varray[i]]) == "undefined"){
paramsset[i] = varray[i];
} else {
paramsset[i] = params[varray[i]];
}
}
//////
// need to modify existing function and return with added parameters
// where I'm stuck as this doesn't work.
newfunc = (function(){
var _func = func;
return function() {
return _func.apply(this, paramsset);
}
})();
newfunc()
}
I'm sure I'm not doing this the correct way, but any help would be appreciated.
I'm certainly not advocating to go towards that solution, but I still implemented something to follow your initial's API design for fun. The signatures weak map is necessary in order to preserve the initial function's signature so that we can call setParams again on partially applied functions.
var setParams = (function () {
var signatures = new WeakMap();
return function (fn, paramsToApply) {
var signature = signatureOf(fn), newFn;
validateParams(paramsToApply, signature.params);
newFn = function () {
var params = appliedParamsFrom(arguments, paramsToApply, signature.indexes);
return fn.apply(this, params);
};
signatures.set(newFn, signature);
return newFn;
};
function signatureOf(fn) {
return signatures.has(fn)?
signatures.get(fn) :
parseSignatureOf(fn);
}
function parseSignatureOf(fn) {
return String(fn)
.match(/function.*?\((.*?)\)/)[1]
.replace(/\s+/g, '')
.split(',')
.reduce(function (r, param, index) {
r.indexes[param] = index;
r.params.push(param);
return r;
}, { indexes: {}, params: [] });
}
function validateParams(paramsToApply, actualParams) {
Object.keys(paramsToApply).forEach(function (param) {
if (actualParams.indexOf(param) == -1) throw new Error("parameter '" + param + "' could not be found in the function's signature which is: 'function (" + actualParams + ")'");
});
}
function appliedParamsFrom(args, paramsToApply, paramsIndex) {
var appliedParams = [],
usedIndexes = [],
argsIndex = 0,
argsLen = args.length,
argSpotIndex = 0;
Object.keys(paramsToApply).forEach(function (param) {
var index = paramsIndex[param];
appliedParams[index] = paramsToApply[param];
usedIndexes.push(index);
});
while (argsIndex < argsLen) {
if (usedIndexes.indexOf(argSpotIndex) == -1) {
appliedParams[argSpotIndex] = args[argsIndex++];
}
++argSpotIndex;
}
return appliedParams;
}
})();
function add(a, b) { return a + b; }
var addTo9 = setParams(add, { b: 9 });
var add10To9 = setParams(addTo9, { a: 10 });
document.write(addTo9(10) + ', ' + add10To9());
Now, note that JavaScript comes with the Function.prototype.bind function which allows to perform in-order partial function application. The first parameter to bind has nothing to do with arguments, it's to bind the this value.
function add(a, b) { return a + b; }
var addTo9 = add.bind(null, 9);
document.write(addTo9(10));
And finally, an implementation with a placholder if you need one:
var partial = (function (undefined) {
var PLACEHOLDER = {};
function partial(fn, partialArgs) {
return function () {
return fn.apply(this, applyPartialArgs(arguments, partialArgs));
};
}
Object.defineProperty(partial, 'PLACEHOLDER', {
get: function () { return PLACEHOLDER; }
});
return partial;
function applyPartialArgs(args, partialArgs) {
var appliedArgs = partialArgs.map(function (arg) {
return arg === PLACEHOLDER? undefined : arg;
}),
partialArgsLen = partialArgs.length,
argsLen = args.length,
argsIndex = 0,
argSpotIndex = 0;
while (argsIndex < argsLen) {
if (
partialArgs[argSpotIndex] === PLACEHOLDER ||
argSpotIndex >= partialArgsLen
) {
appliedArgs[argSpotIndex] = args[argsIndex++];
}
++argSpotIndex;
}
return appliedArgs;
}
})();
function add(a, b, c, d) {
return a + b + c + d;
}
var _ = partial.PLACEHOLDER;
var addTo9 = partial(add, [_, 5, _, 4]);
document.write(addTo9(5, 5));
I'm guessing that they might have been testing for knowledge of partial application. (not currying)
Edit: Edited based upon your comments. This is Crockford's curry function straight from his book.
function add(a, b) {
return a + b;
}
if (!Function.prototype.partial) {
Function.prototype.partial = function() {
var slice = Array.prototype.slice,
args = new Array(arguments.length),
that = this;
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return function() {
return that.apply(null, args.concat(slice.apply(arguments)));
}
};
}
var example = add.partial(4);
console.log(example(10)); // output 14
console.log(example(20)); // output 24
var example = adder(4) assigns example to be function with a closure with a (in this case 4). When example is called like in the console.log, it will in effect be returning "the value of a when example was assigned, plus this new number."
Walkthrough of the partial() function:
Converts arguments to an array
returns a function gets passed the arguments given, which can be called later. It has a closure with the previously assigned arguments.

A Javascript function which creates an object which calls the function itself

I am trying to make an angular service that returns a new object.
That's fine and good and works. new MakeRoll() creates an instance. But self.add, near the end also calls new MakeRoll() and that doesn't create an instance when I call add like I think it should.
I'm probably doing this all wrong but I haven't been able to figure it out.
var services = angular.module('services', []);
services.factory('Roll', [function() {
var MakeRoll = function () {
var self = {};
self.rolls = [];
self.add = function(number, sizeOfDice, add) {
var newRoll = {};
newRoll.number = number || 1;
newRoll.sizeOfDice = sizeOfDice || 6;
newRoll.add = add || 0;
newRoll.rollDice = function() {
var result = 0;
var results=[];
for (var i = 0; i < newRoll.number; i++) {
var roll = Math.floor(Math.random() * newRoll.sizeOfDice) + 1;
result += roll;
results.push(roll);
}
newRoll.results = results;
newRoll.result = result;
newRoll.Roll = new MakeRoll();
};
self.rolls.push(newRoll);
return self;
};
self.remove = function(index) {
self.rolls.splice(index, 1);
};
self.get = function(index) {
return self.rolls[index];
};
return self;
};
return new MakeRoll();
}
]);
angular service is designed to be singleton to accomplish some business logic, so don't mix up plain model with angular service. if you want to have more objects, just create a constructor and link it in service to be operated on.
function MakeRoll() {
...
}
angular.module('service', []).factory('Roll', function () {
var rolls = [];
return {
add: add,
remove: remove,
get: get
}
function add() {
// var o = new MakrRoll();
// rolls.push(o);
}
function remove(o) {
// remove o from rolls
}
function get(o) {
// get o from rolls
}
});

Categories

Resources