Javascript: Object method - why do I need parentheses? - javascript

I am learning javascript and Ive stumbled upon issue that I do not understand. Could somebody explain to me why in method compareDNA I need to use parentheses while using this.dna and in the previous method it works just fine?
// Returns a random DNA base
const returnRandBase = () => {
const dnaBases = ['A', 'T', 'C', 'G'];
return dnaBases[Math.floor(Math.random() * 4)];
};
// Returns a random single stand of DNA containing 15 bases
const mockUpStrand = () => {
const newStrand = [];
for (let i = 0; i < 15; i++) {
newStrand.push(returnRandBase());
}
return newStrand;
};
function pAequorFactory(specimenNum, dna){
return {
specimenNum,
dna,
mutate(){
let i = Math.floor(Math.random() * 15)
let newGene = returnRandBase()
while (this.dna[i] === newGene){
newGene = returnRandBase()
}
this.dna[i] = newGene
return this.dna
},
compareDNA(object){
let counter = 0
for(let i = 0; i < this.dna().length; i++){
if(this.dna()[i] === object.dna()[i]){
counter++
}
}
let percentage = counter / this.dna().length * 100
return `Specimen number ${this.specimenNum} and specimen number ${object.specimenNum} have ${percentage}% of DNA in common.`
},
}
}
let aligator = pAequorFactory(1, mockUpStrand)
let dog = pAequorFactory(2, mockUpStrand)
console.log(aligator.compareDNA(dog))
console.log(dog.dna().length)

The problem is that the dna that is passed as an argument is a function, so it becomes a method of the returned object, and needs to be called with .dna(). However, this looks like a mistake - actually an array should have been passed:
let aligator = pAequorFactory(1, mockUpStrand())
// ^^
let dog = pAequorFactory(2, mockUpStrand())
// ^^
Then you can access .dna[i] or .dna.length as normal.
If you don't do that, dog.dna() returns a different DNA every time, which doesn't make sense.
using this.dna and in the previous method it works just fine?
Actually, it doesn't. dog.mutate() does return a function with a single integer property. It's supposed to return an array really.

Related

I generate 2 arrays of equal length (15), with a choice of 4 possible letters at each position - why do they come back 53% to 87% similar?

This is inspired by a Codecadamey project, where I'm learning JavaScript.
The problem is meant to simulate a simple DNA strand. It has 15 positions in the array, and those elements are either an A, T, C, or G to represent DNA bases.
There are no limits to the amount of times a single letter (base) can show up in the array.
I create 30 arrays that are made up of at least 60% C and/or G in any of the positions, these are meant to represent strong DNA strands.
I compare the strands to each other to see what % match they are. I consider a 'match' being true when there is the same base at the same position thisDNA[i] === comparisonDNA[i]
When I test a batch of 30 of these 'strong' samples to see the best and worst match levels, I find the results very tightly grouped (I ran it 3,000 times and the highest was 87%, lowest 53%), yet it is very easy for me to concieve of two samples that will survive that are a 0% match:
const sample1 = AGACCGCGCGTGGAG
const sample2 = TCTGGCGCGCACCTC
(obviously I've cheated by building these to be a 0% match and not randomly generating them)
Here's the full code: https://gist.github.com/AidaP1/0770307979e00d4e8d3c83decc0f7771
My question is as follows: Why is the grouping of matches so tight? Why do I not see anything below a 53% match after running the test thousands of times?
Full code:
// Returns a random DNA base
const returnRandBase = () => {
const dnaBases = ['A', 'T', 'C', 'G']
return dnaBases[Math.floor(Math.random() * 4)]
}
// Returns a random single stand of DNA containing 15 bases
const mockUpStrand = () => {
const newStrand = []
for (let i = 0; i < 15; i++) {
newStrand.push(returnRandBase())
}
return newStrand
}
const pAequorFactory = (num, arr) => { //factory function for new strand specimen
return {
specimenNum: num,
dna: arr,
mutate() {
//console.log(`old dna: ${this.dna}`) //checking log
let randomBaseIndex = Math.floor(Math.random() * this.dna.length) /// chooses a location to exchange the base
let newBase = returnRandBase()
while (newBase === this.dna[randomBaseIndex]) { // Rolls a new base until newBase !== current base at that position
newBase = returnRandBase()
}
this.dna[randomBaseIndex] = newBase;
//console.log(`New dna: ${this.dna}`) //checking log
return this.dna;
},
compareDNA(pAequor) { // compare two strands and output to the console
let thisDNA = this.dna;
let compDNA = pAequor.dna;
let matchCount = 0
for (i = 0; i < this.dna.length; i++) { //cycles through each array and log position + base matches on matchCount
if (thisDNA[i] === compDNA[i]) {
matchCount += 1;
};
};
let percMatch = Math.round(matchCount / this.dna.length * 100) //multiply by 100 to make 0.25 display as 25 in console log
console.log(`specimen #${this.specimenNum} and specimen #${pAequor.specimenNum} are a ${percMatch}% DNA match.`)
return percMatch;
},
compareDNAbulk(pAequor) { //as above, but does not log to console. Used for large arrays in findMostRelated()
let thisDNA = this.dna;
let compDNA = pAequor.dna;
let matchCount = 0
for (i = 0; i < this.dna.length; i++) {
if (thisDNA[i] === compDNA[i]) {
matchCount += 1;
};
};
let percMatch = Math.round(matchCount / this.dna.length * 100) //multiply by 100 to make 0.25 display as 25 in console log
return percMatch;
},
willLikelySurvive() { // looks for >= 60% of bases as either G or C
let countCG = 0;
this.dna.forEach(base => {
if (base === 'C' || base === 'G') {
countCG += 1;
}
})
//console.log(countCG) // testing
//console.log(this.dna) // testing
return countCG / this.dna.length >= 0.6
},
complementStrand() {
return this.dna.map(base => {
switch (base) {
case 'A':
return 'T';
case 'T':
return 'A';
case 'C':
return 'G';
case 'G':
return 'C';
}
})
} //close method
} // close object
} // close factory function
function generatepAequorArray(num) { // Generatess 'num' pAequor that .willLikelySurvive() = true
let pAequorArray = []; //result array
for (i = 0; pAequorArray.length < num; i++) {
let newpAequor = pAequorFactory(i, mockUpStrand()); // runs factory until there are 30 items in the result array
if (newpAequor.willLikelySurvive() === true) {
pAequorArray.push(newpAequor)
}
}
return pAequorArray;
}
function findMostRelated(array) { // champion/challenger function to find the most related specimens
let winningScore = 0;
let winner1;
let winner2;
for (let i = 0; i < array.length; i++) {
for (let j = i; j < array.length; j++) // j = i to halve the number of loops. i = 0, j = 5 is the same as i = 5, j = 0
if (i !== j) { // Do not check specimen against itself
let currentScore = array[i].compareDNAbulk(array[j]);
if (currentScore > winningScore) { // Challenger becomes the champion if they are a closer match
winningScore = currentScore;
winner1 = array[i].specimenNum;
winner2 = array[j].specimenNum;
}
}
}
let resultArray = [winner1, winner2, winningScore] // stored together for easy return
//console.log(`The most related specimens are specimen #${winner1} and specimen #${winner2}, with a ${winningScore}% match.`)
return resultArray
}
function multiArray(loops) { //test by running finding the closest match in 30 random 'will survive' samples, repaeated 1000 times. Returns the highest and lowest match across the 1000 runs
let highScore = 0;
let lowScore = 100
for (let i = 0; i < loops; i++) {
let pAequorArray = generatepAequorArray(30);
let currentArray = findMostRelated(pAequorArray);
highScore = Math.max(highScore, currentArray[2])
lowScore = Math.min(lowScore, currentArray[2])
}
return results = {
'high score': highScore,
'low score': lowScore
}
}
console.log(multiArray(10000))

Javascript adding multiple arrays in a loop

I am trying to add multiple arrays in javascript.
Here are my arrays I have made, and are working.
function getAmountSpent(){
var amountSpent = ((Math.random() * 500) + 1);
return amountSpent.toFixed(2)
}
function getGift(){
var gift = ((Math.random()* 50) + 1);
return gift.toFixed(2)
}
var names = ["Jeremy","Arun","Alisa","Rohan","Dana"];
var spent = [];
for (let i = 0; i < 5; i++) {
spent.push(getAmountSpent());
}
var gifts = [];
for (let i = 0; i<5; i++) {
gifts.push(getGift());
}
What I need help with is adding these arrays in a new function. I have began writing the code, and I am not sure what is wrong.
var totals =[];
for (let i=0; i<5; i++) {
totals.push(getSumTotals())
}
function getSumTotals(a){
totals= spent+(spent * gifts);
return totals.toFixed(2)
}
From what you can see, I am trying to add up the totals much like this:
totals[0] = spent[0] + (spent[0] * gifts[0]);
totals[1] = spent[1] + (spent[1] * gifts[1]);
totals[2] = spent[2] + (spent[2] * gifts[2]);
totals[3] = spent[3] + (spent[3] * gifts[3]);
totals[4] = spent[4] + (spent[4] * gifts[4]);
if it helps, the professor added guided instructions for function getSumTotals(a) stating:
This function will return the sum of the elements in array a.
You will be passing the array that holds your totals to
the parameter a. Be sure to treat the values in a as numbers.
I am not sure if this helps but here is the output to my document.
Current Total should equal (spent) + (spent * gifts). For instance, for Jeremy in this example, current total should equal:
$36.55 + ($36.55*0.0626) = $38.83. Since there are many variables involved, I am not 100% sure what I should write for function getSumTotals(a)
The parameter "a" is a placeholder because I am not sure how many parameter values I need, and the proper format I need to use.
As for the code...
You're both
not passing an index to getSumTotals
not using this parameter within getSumTotals to access your spent and gifts arrays
var totals =[];
for (let i=0; i<5; i++) {
totals.push(getSumTotals(i)) // you were missing i
}
function getSumTotals(idx) { // I took liberties to rename this
totals = spent[idx] + (spent[idx] * gifts[idx]);
return totals.toFixed(2);
}
Now for the Math...
All that said, this math of spent[i] + spent[i] * gifts[i] doesn't make much sense either. Was this specified in the problem?
you may use like this
defined gifts
gifts=[45,43,32];
defined spends
spends=[43,32,21];
this is the getSumTotal funtion
getSumTotal=(x)=>(x.a+x.b)
this is where added
totals=gifts.map((d1,i)=>{
return fu({a:gifts[i],b:spends[i]})
})
I understand this is your assignment, however - if the idea is to both generate arrays, and then add them together, it is a redundant step. Just use the name array to iterate once and do all your calculations within that single loop.
Here, I had some fun and took some liberties, but hopefully you see why multiple arrays are redundant.
function getSumTotals() {
const getAmountSpent = () => Math.random() * 500 + 1;
const getGift = () => Math.random() * 50 + 1;
const names = ["Jeremy", "Arun", "Alisa", "Rohan", "Dana"];
let totals = []
names.forEach((name, i) => {
let spent = getAmountSpent()
let gifts = getGift()
let $$$ = (spent + spent * gifts).toFixed(2);
totals[i] = $$$
console.log(`${name} cost me $${$$$}${'!'.repeat(($$$/1000) | 1)}`)
});
return totals;
}
getSumTotals()
Note, that toString returns a type of "String", but not "Number".
When you try to sum a number with string, you get a concatenated string "1" + 2 = "12"
To turn a string into Number, you must use a Number("str") function, or just a bunary + before the string:
console.log( "1" + 2 );
console.log( Number("1") + 2 );
console.log( +"1" + 2 );
Also, you use the same loop 3 times, but can use just one loop instead, and call all functions inside the one loop. And use your array.length instead of fixed number 5:
let names = ["Jeremy", "Arun", "Alisa", "Rohan", "Dana"];
let spent = [];
let gifts = [];
let totals = [];
for (let i = 0; i < names.length; i++) {
spent.push( getAmountSpent() );
gifts.push( getGift() );
totals.push( getSumTotals(i) );
}
console.log( totals );
function getAmountSpent() {
return rand(1, 500, 2);
}
function getGift() {
return rand(1, 50, 2);
}
function getSumTotals(i) {
return +( spent[i] * ( 1 + gifts[i] ) ).toFixed(2);
}
function rand(from, to, fixed = 0){
return +(Math.random()*( to - from ) + from).toFixed(fixed);
}
P.s. Math.random() returns a number between 0 (included) and 1 (not included). If you need a random number between (example) 20 and 100, Math.random()*(100-20) will give a number between 0 and 80. After adding +20 to the result, you get a number from 20 to 100. That's what does this formula Math.random()*( to - from ) + from
P.P.s. Another way, to get the same thing:
var names = ["Jeremy", "Arun", "Alisa", "Rohan", "Dana"].reduce( (prev, elem) => {
let spent = rand(1, 500, 2);
let gift = rand(1, 50, 2);
prev[elem] = new UserData( spent, gift );
return prev;
}, {});
console.log( "Jeremy spent: " + names.Jeremy.spent );
console.log( names );
function rand(from, to, fixed = 0){
return +(Math.random()*( to - from ) + from).toFixed(fixed);
}
function UserData(spent, gift){
this.spent = spent;
this.gift = gift;
this.total = +(spent * ( 1 + gift )).toFixed(2);
}
/* Google → Array reduce, Constructor functions */
function getAmountSpent(){
let amountSpent = ((Math.random() * 500) + 1);
return Number(amountSpent.toFixed(2))
}
function getGift(){
let gift = ((Math.random()* 50) + 1);
return Number(gift.toFixed(2))
}
let names = ["Jeremy","Arun","Alisa","Rohan","Dana"];
let spent = [];
let gifts = [];
let totals =[];
for (let i = 0; i < names.length; i++) {
spent.push(getAmountSpent());
gifts.push(getGift());
totals[i] = (spent[i]+(spent[i] * gifts[i])).toFixed(2);
totals[i] = parseFloat(totals[i])
}
Hi there
I don't think you need a function to add the totals. you just need to loop through and assign totals[i] to spent[i] + (spent[i] * gifts[i]).
then you can use the parseFloat and toFixed function to change the string to a number. remember toFixed() function turns numbers to string. so you need to use the parseFloat to change it to number again as shown in the code above. or you can come up with an easy way of changing it to number. I hope this helps!

How do I check if 2 numbers are the same from Math.random [duplicate]

Can't seem to find an answer to this, say I have this:
setInterval(function() {
m = Math.floor(Math.random()*7);
$('.foo:nth-of-type('+m+')').fadeIn(300);
}, 300);
How do I make it so that random number doesn't repeat itself. For example if the random number is 2, I don't want 2 to come out again.
There are a number of ways you could achieve this.
Solution A:
If the range of numbers isn't large (let's say less than 10), you could just keep track of the numbers you've already generated. Then if you generate a duplicate, discard it and generate another number.
Solution B:
Pre-generate the random numbers, store them into an array and then go through the array. You could accomplish this by taking the numbers 1,2,...,n and then shuffle them.
shuffle = function(o) {
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
var randorder = shuffle([0,1,2,3,4,5,6]);
var index = 0;
setInterval(function() {
$('.foo:nth-of-type('+(randorder[index++])+')').fadeIn(300);
}, 300);
Solution C:
Keep track of the numbers available in an array. Randomly pick a number. Remove number from said array.
var randnums = [0,1,2,3,4,5,6];
setInterval(function() {
var m = Math.floor(Math.random()*randnums.length);
$('.foo:nth-of-type('+(randnums[m])+')').fadeIn(300);
randnums = randnums.splice(m,1);
}, 300);
You seem to want a non-repeating random number from 0 to 6, so similar to tskuzzy's answer:
var getRand = (function() {
var nums = [0,1,2,3,4,5,6];
var current = [];
function rand(n) {
return (Math.random() * n)|0;
}
return function() {
if (!current.length) current = nums.slice();
return current.splice(rand(current.length), 1);
}
}());
It will return the numbers 0 to 6 in random order. When each has been drawn once, it will start again.
could you try that,
setInterval(function() {
m = Math.floor(Math.random()*7);
$('.foo:nth-of-type(' + m + ')').fadeIn(300);
}, 300);
I like Neal's answer although this is begging for some recursion. Here it is in java, you'll still get the general idea. Note that you'll hit an infinite loop if you pull out more numbers than MAX, I could have fixed that but left it as is for clarity.
edit: saw neal added a while loop so that works great.
public class RandCheck {
private List<Integer> numbers;
private Random rand;
private int MAX = 100;
public RandCheck(){
numbers = new ArrayList<Integer>();
rand = new Random();
}
public int getRandomNum(){
return getRandomNumRecursive(getRand());
}
private int getRandomNumRecursive(int num){
if(numbers.contains(num)){
return getRandomNumRecursive(getRand());
} else {
return num;
}
}
private int getRand(){
return rand.nextInt(MAX);
}
public static void main(String[] args){
RandCheck randCheck = new RandCheck();
for(int i = 0; i < 100; i++){
System.out.println(randCheck.getRandomNum());
}
}
}
Generally my approach is to make an array containing all of the possible values and to:
Pick a random number <= the size of the array
Remove the chosen element from the array
Repeat steps 1-2 until the array is empty
The resulting set of numbers will contain all of your indices without repetition.
Even better, maybe something like this:
var numArray = [0,1,2,3,4,5,6];
numArray.shuffle();
Then just go through the items because shuffle will have randomized them and pop them off one at a time.
Here's a simple fix, if a little rudimentary:
if(nextNum == lastNum){
if (nextNum == 0){nextNum = 7;}
else {nextNum = nextNum-1;}
}
If the next number is the same as the last simply minus 1 unless the number is 0 (zero) and set it to any other number within your set (I chose 7, the highest index).
I used this method within the cycle function because the only stipulation on selecting a number was that is musn't be the same as the last one.
Not the most elegant or technically gifted solution, but it works :)
Use sets. They were introduced to the specification in ES6. A set is a data structure that represents a collection of unique values, so it cannot include any duplicate values. I needed 6 random, non-repeatable numbers ranging from 1-49. I started with creating a longer set with around 30 digits (if the values repeat the set will have less elements), converted the set to array and then sliced it's first 6 elements. Easy peasy. Set.length is by default undefined and it's useless that's why it's easier to convert it to an array if you need specific length.
let randomSet = new Set();
for (let index = 0; index < 30; index++) {
randomSet.add(Math.floor(Math.random() * 49) + 1)
};
let randomSetToArray = Array.from(randomSet).slice(0,6);
console.log(randomSet);
console.log(randomSetToArray);
An easy way to generate a list of different numbers, no matter the size or number:
function randomNumber(max) {
return Math.floor(Math.random() * max + 1);
}
const list = []
while(list.length < 10 ){
let nbr = randomNumber(500)
if(!list.find(el => el === nbr)) list.push(nbr)
}
console.log("list",list)
I would like to add--
var RecordKeeper = {};
SRandom = function () {
currTimeStamp = new Date().getTime();
if (RecordKeeper.hasOwnProperty(currTimeStamp)) {
RecordKeeper[currTimeStamp] = RecordKeeper[currTimeStamp] + 1;
return currTimeStamp.toString() + RecordKeeper[currTimeStamp];
}
else {
RecordKeeper[currTimeStamp] = 1;
return currTimeStamp.toString() + RecordKeeper[currTimeStamp];
}
}
This uses timestamp (every millisecond) to always generate a unique number.
you can do this. Have a public array of keys that you have used and check against them with this function:
function in_array(needle, haystack)
{
for(var key in haystack)
{
if(needle === haystack[key])
{
return true;
}
}
return false;
}
(function from: javascript function inArray)
So what you can do is:
var done = [];
setInterval(function() {
var m = null;
while(m == null || in_array(m, done)){
m = Math.floor(Math.random()*7);
}
done.push(m);
$('.foo:nth-of-type('+m+')').fadeIn(300);
}, 300);
This code will get stuck after getting all seven numbers so you need to make sure it exists after it fins them all.

How can generate numbers from given number?

Im trying to make a generator which will keep generate numbers from a number that i give..
Let's say:
function GenerateCombinations(data) {
var x = number; //0122
// So i want to generate numbers starting from that one i set.
// For example:
// 0123
// 0124
// 0125
// ...
// 0129
// 0130
// 0131
// ...
// 0199
// 0200
}
GenerateCombinations("0122");
How can i archive it? :/ May i split it, and use setInterval or something?
Sorry i am noob at this tho.
You could use a generator and take the number as new next value.
function* generate(value) {
var temp;
while (true) {
temp = yield ++value;
if (temp !== undefined) {
value = temp;
}
}
}
var iterator = generate(122);
console.log(iterator.next().value);
console.log(iterator.next().value);
console.log(iterator.next(300).value);
console.log(iterator.next().value);
console.log(iterator.next(0).value);
console.log(iterator.next().value);
You could use a real generator function:
function* range(start) {
let value = +start;
while(true) {
const str = "" + value;
yield "0".repeat(start.length - str.length) + str;
value++;
}
}
You can't of course generate an infinite number of values, you need to choose how many new values you want to have. This can easily be achieved with a for loop. Reference ad W3
Here we take the starting value x and assign it to our counter i, it will cycle untile i mets the numbersToGenerate which is the amount of values you want to generate.
for (i = x; i < numbersToGenerate; i++) {
// print your new value or manipulate it as you wish
}

Counting in binary

I'm only learning to program and was trying to make a program to count in binary.
I made a function that could convert the provided decimal to binary and it looks to be working just ok, but when I try to count upwards using a for loop my browser freezes and I can't understand why. Using a similar while loop produces the result I needed.
The problem is right at the bottom commented out. Please help figure out what I'm doing wrong here.
Here's my code:
function isOdd(num) {return num % 2};
var toBinary = function (number) {
ints = [];
binary = [];
ints.push(Math.floor(number));
while (number >= 1) {
number = (Math.floor(number))/2;
ints.push(Math.floor(number));
}
for (i=ints.length-1;i>=0;i--) {
if (isOdd(ints[i])) {
binary.push(1);
} else {
binary.push(0);
}
}
if (binary[0] === 0) {
binary.splice(0,1);
}
return binary;
};
var count = 0;
while (count <= 50) {
console.log(toBinary(count));
count++;
}
/*
for (i=1;i<=50;i++) {
console.log(toBinary(i));
}
*/
Use for (var i=ints.length-1;i>=0;i--) and for (var i=1;i<=50;i++), otherwise i will be a global variable and it will be overwritten inside toBinary.
You haven't declared i in the toBinary function, hence i is redefined on every call of toBinary in the last loop, and i will never reach 50.
Use var to declare variables like so:
var toBinary = function (number) {
var ints = [],
binary = [],
i;
:
}

Categories

Resources