Count amount of time a number appears in an Array - javascript

So I have a dice rolling function. I want to roll 2 dice 20 times, each time I roll, I want to add those numbers, and then I want to see how many times the sum appears, and put that number into an array.
function Dice() {
this.roll = function() {
var randomValue = Math.ceil(Math.random() * 6);
this.side = randomValue;
return this.side;
}
}
var dice1 = new Dice();
var dice2 = new Dice();
function getAmount() {
var finalArray = [];
function diceSum() {
var sum = dice1.roll() + dice2.roll();
return sum;
}
var trackSum = [];
for (var i = 1; i <= 20; i++) {
trackSum.push(diceSum());
}
var reduced = trackSum.reduce(function(acc, sum, i, arr) {
return acc.i += sum;
}, {});
return reduced;
}
so I get trackSum which has 20 numbers in an array which, each number is the sum of the 2 dice rolled. If sum = 2 and it appears 5 times, sum = 4, appears 2 times, sum = 3, appears 1 time, final array should look like
[5, 2, 1]
So far I tried a reduce method, filter, and forEach. I just can't figure out how to compare the numbers to see how many times it's appearing. Any help would be appreciated. Thanks!

Use an object whose keys are the sums, and values are the number of times the sum appears. Loop through trackSum and increment the value of the corresponding element.
var freq = {};
trackSum.forEach(function(sum) {
freq[sum] = freq[sum] ? freq[sum] + 1 : 1;
}
console.log(freq);

Apologies for answering my own question, but after some googling, I figured it out. So don't pay attention to the reduce method I have in my original post. This is what I did instead:
var countedSum = trackSum.reduce(function(emptyObj, sum, i, arr){
if (sum in emptyObj){
emptyObj[sum]++
} else {
emptyObj[sum] = 1
}
return emptyObj;
}, {});
This returns an object, with the sum value as the key, and the amount of times it happens as the value.
Then I learned a new method that grabs the values and puts it in an array.
Object.values(countedSum)
That solves it!
Thanks #Barmar for your help!

Related

Javascript array.length returning double the value it's supposed to return. Why?

So, I'm learning Javascript through a book and it has some exercises. One of the exercises asks for you to build two functions, one that creates an array from two numbers provided in the arguments, and the other function has to sum all the numbers in the array. Here's my code:
let beg = 1;
let end = 3;
array = [];
sumNum = 0;
function range(begg, endd) {
for (let count = begg; count <= endd; count++) {
array.push(count);
}
return array;
}
console.log(range(beg, end));
function sum(arrayy) {
for (let i = 0; i <= arrayy.length - 1; i++) {
sumNum = arrayy[i] + sumNum;
console.log(sumNum);
}
console.log("\n")
console.log(arrayy.length - 1);
return sumNum / 2;
}
console.log(sum(range(beg, end)));
array2 = [1, 2, 3];
console.log("\n");
console.log(array2.length);
As I was solving the exercise I kept getting double the sum of all the numbers in the array. I started to print some information and discovered that my arrayy.length is returning double the value it's supposed to return and the loop runs double the times it should run.
Here's my output:
[ 1, 2, 3 ]
1
3
6
7
9
12
5
6
3
Sorry it this is a noob question, but my curiosity is killing me and I have not found anything on the internet, so why am I getting this result?
Thanks in advance.
As Ivan said: The "array" variable is global, so each time you call the range function you keep appending items to that shared array. You should add the array inside your function and return it. Other than that you did a pretty nice job!
function range(begg, endd) {
let array = []
for (let count = begg; count <= endd; count++) {
array.push(count);
}
return array;
}
Also: The sum function should have the "sumnum" variable inside the function to prevent it from increasing every time you call the function:
function sum(arrayy) {
let sumnum = 0;
for (let i = 0; i <= arrayy.length - 1; i++) {
sumNum = arrayy[i] + sumNum;
console.log(sumNum);
}
console.log("\n");
console.log(arrayy.length - 1);
return sumNum / 2;
}
remove the array and sumnum variables from the top of your code to get rid of the global variables.

Bird Watcher Exercism Javascript Solution - counter not counting

I'm trying to figure out an exercise from Exercism's Javascript track. It is an exercise using for loops called Bird Watcher.
The instructions say to initialize a function called birdsInWeek that takes two arguments. First is an array 'birdsPerDay', and the second is 'week'. Given the birdsPerDay array, the function is supposed to count the total number of birds seen in the specified week. So, if 'week' = 1, for example, the corresponding indexes of 'birdsPerDay' to add together would be 0 - 6, and so on and so forth.
I have tested my code using the provided test cases and in the Chrome console. I tried logging some values to understand where the bug is, but I can't figure out why my counter (named 'totalBirdsInSpecifiedWeek'), which is initialized to '0' is staying at '0' instead of adding the corresponding indexes of 'week' together and return the correct 'totalBirdsInSpecifiedWeek'. I have tried changing the placement of the return statement as well, but that didn't result in any change.
Below is the code I have written:
export function birdsInWeek(birdsPerDay, week) {
let totalBirdsInSpecifiedWeek = 0
if (week === 1) {
for (let i = 0; i < birdsPerDay[7]; i++) {
totalBirdsInSpecifiedWeek += birdsPerDay[i];
}
return totalBirdsInSpecifiedWeek;
} else {
for (let i = week * 7 - 7; i < birdsPerDay[week * 7]; i++) {
totalBirdsInSpecifiedWeek += birdsPerDay[i];
}
return totalBirdsInSpecifiedWeek;
};
}
Where did I go wrong?
birdsPerDay[7] this means values of birdsPerDay array at index 7 of the array
so the condition would loop checking if i is lesser than that value sometimes it would even hit the NaN so the idea is to just check on either length or index of the array to get an accurate response ..
Per the commenter #gog, I changed the 4th and 9th lines of code to correct the stopping condition in each of the for loops so they are written as indexes of the 'birdsPerDay' array and instead just numerical values.
export function birdsInWeek(birdsPerDay, week) {
let totalBirdsInSpecifiedWeek = 0
if (week === 1) {
for (let i = 0; i < 7; i++) {
totalBirdsInSpecifiedWeek += birdsPerDay[i];
}
return totalBirdsInSpecifiedWeek;
} else {
for (let i = week * 7 - 7; i < week * 7; i++) {
totalBirdsInSpecifiedWeek += birdsPerDay[i];
}
return totalBirdsInSpecifiedWeek;
}; return totalBirdsInSpecifiedWeek;
}
I found the exercise, according to the problem posed, I think this is the solution.
bird-watcher - exercism
Explanation, the array is divided by the days of the week, then the array is divided by that amount from 0 to 6 (Monday to Sunday), then I used a nested "for" to iterate over the corresponding week, I used another "for" to do the addition, return the value from the function and return the result.
let countsbirdsPerDay = [2, 5, 0, 7, 4, 1, 3, 0, 2, 5, 0, 1, 3, 1];
let userWeek = 2;
const birdsInWeek = (birdsPerDay, week) => {
let segmentWeek = Math.round(birdsPerDay.length / week);
let total = 0;
for (let i = 0; i < week; i++) {
views = birdsPerDay.slice(i * segmentWeek, segmentWeek * (i + 1));
if ((i + 1) == week) {
for (let x = 0; x < views.length; x++) {
total = total + views[x];
}
}
}
return total;
}
console.log(birdsInWeek(countsbirdsPerDay, userWeek));

Most frequently occuring number (mode) in a list - want to get only the highest value

I'm trying to get whatever number is the most frequently occuring number in an array, so for an array containing 1,2,10,5,1 the result should be 1. The code I wrote returns me the frequency for each number, so 1 occurs twice, 2 occurs once, 10 occurs once etc. Any suggestions how I can fix my result?
function mode(arr) {
var uniqNum = {};
var numCounter = function(num, counter) {
if(!uniqNum.hasOwnProperty(num)) {
uniqNum[num] = 1;
} else {
uniqNum[num] ++;
}
};
arr.forEach(numCounter);
return uniqNum;
}
I've kept your code unchanged and added some extra statements. Here is the demo: http://codepen.io/PiotrBerebecki/pen/rrdxRo
function mode(arr) {
var uniqNum = {};
var numCounter = function(num, counter) {
if(!uniqNum.hasOwnProperty(num)) {
uniqNum[num] = 1;
} else {
uniqNum[num] ++;
}
};
arr.forEach(numCounter);
return Object.keys(uniqNum)
.sort((a,b) => uniqNum[b] - uniqNum[a]) // sort by frequency
.filter((val,ind,array) => uniqNum[array[0]] == uniqNum[val]) // leave only most frequent
.map(val => Number(val)); // convert text to number
}
console.log( JSON.stringify(mode([3,3,2,4,4])) ) // [3,4]
console.log( JSON.stringify(mode([2,4,3,3])) ) // [3]
I think it could be done only with a little modification to your forEach loop and the assistance of another auxiliary data structure:
function mode(arr) {
var freq = [], uniqNum = {}, i;
arr.forEach(function (num) {
uniqNum[num] = i = (uniqNum[num] || 0) + 1;
freq[i] = (freq[i] || []).concat(num);
});
return freq[freq.length - 1];
}
console.log(mode([1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 6, 7, 1, 6]));
With only one iteration over all the elements of the array we can gather enough information to print out the result:
uniqNum is the set you created to gather info about the element's frequency.
freq will be an array which last element will contain an array with the elements of higher frequency.
Fiddle. Hope it helps.
First we want to make an array where we count the number of occurrences of a certain value up to that point.
Then we use the reduce function to return an array of values read from the original array for the indexes whose values have the current max appearances. We redefine max and empty the final output array of modes (if new max is established) as we go along. We want this to be a collection in case there is a tie for maximum appearances.
Additional advantage of the below is that it doesn't require sort which is more expensive o(nlog n) and keeps the time complexity down to just linear. I also wanted to keep the functions used down to only two (map and reduce) as it is all that is need in this case.
edit: fixed a major bug uniqNum[e] += 1 instead of uniqNum[e] + 1 which went unnoticed as my initial case array was still returning expected result. Also made the syntax more concise in favor of more comments.
var arr = [1,2,10,5,1,5,2,2,5,3,3];
//global max to keep track of which value has most appearances.
var max = -1;
var uniqNum = {};
var modeArray = arr.map(function(e) {
//create array that counts appearances of the value up to that point starting from beginning of the input arr array.
if(!uniqNum.hasOwnProperty(e)) {
uniqNum[e] = 1;
return 1;
} else {
return uniqNum[e] += 1;
}
//reduce the above appearance count array into an array that only contains values of the modes
}).reduce(function (modes, e1, i) {
//if max gets beaten then redefine the mode array to only include the new max appearance value.
if(e1 > max){
//redefining max
max = e1;
//returning only the new max element
return [arr[i]];
//if its a tie we still want to include the current value but we don't want to empty the array.
}else if(e1 == max){
//append onto the modes array the co-max value
return[...modes, arr[i]];
}
return modes;
},[]);
alert(modeArray);
Here is a test you can run of my solution against #acontell. In my browser (Chrome with V8) my solution was around three-four times faster for arrays with large number of repeating values and even bigger advantage with distributions with lower number of repeating values. #acontell 's is a cleaner looking solution for sure, but definitely not faster in execution.
var arr = [];
for(var i=0; i < 100000; i++){
arr.push(Math.floor(Math.random() * (100 - 1)) + 1);
}
console.time("test");
test();
function test(){
var max = -1;
var uniqNum = {};
var modeArray = arr.map(function(e) {
//create array that counts appearances of the value up to that point starting from beginning of the input arr array.
if(!uniqNum.hasOwnProperty(e)) {
uniqNum[e] = 1;
return 1;
} else {
return uniqNum[e] += 1;
}
//reduce the above appearance count array into an array that only contains values of the modes
}).reduce(function (modes, e1, i) {
//if max gets beaten then redefine the mode array to only include the new max appearance value.
if(e1 > max){
//redefining max
max = e1;
//returning only the new max element
return [arr[i]];
//if its a tie we still want to include the current value but we don't want to empty the array.
}else if(e1 == max){
//append onto the modes array the co-max value
modes.push(arr[i])
return modes;
}
return modes;
},[]);
}
console.timeEnd("test");
console.time("test1");
test1();
function test1 () {
var freq = [],
uniqNum = {},
i;
arr.forEach(function(num) {
uniqNum[num] = i = (uniqNum[num] || 0) + 1;
freq[i] = (freq[i] || []).concat(num);
});
return freq[freq.length - 1];
}
console.timeEnd("test1");
I've tried as an exercise to solve this with native js functions.
var arr = [1,2,10,5,1];
// groupBy number
var x = arr.reduce(
function(ac, cur){
ac[cur]?(ac[cur] = ac[cur] + 1):ac[cur] = 1;
return ac;
}, {}
);
// sort in order of frequencies
var res = Object.keys(x).sort(
function(a,b){ return x[a] < x[b]}
);
res[0] has the most frequent element

Assigning value to id only once

I've a problem that has been bugging me for a while now.
I have 14 divs each which must be assigned a random ID (between 1 and 14) each time the page loads.
Each of these divs have the class ".image-box" and the format of the ID I'm trying to assign is 'box14'
I have the JS code working to assign random IDs but I'm having trouble not getting the same ID to assign twice.
JavaScript
var used_id = new Array();
$( document ).ready(function() {
assign_id();
function assign_id()
{
$('.image-box').each(function (i, obj) {
random_number();
function random_number(){
number = 2 + Math.floor(Math.random() * 14);
var box_id = 'box' + number;
if((box_id.indexOf(used_id) !== -1) === -1)
{
$(this).attr('id',box_id);
used_id.push(box_id);
}
else
{
random_number();
}
}
});
}
});
Thanks for your help,
Cheers
Mmm, random...
Instead of using a randomly generated number (which, as your experiencing, may randomly repeat values) just use an incrementally-updated counter when assigning IDs.
function assign_id() {
var counter = 0;
$('.image-box').each(function (i, obj) {
$(this).attr('id','image-box-' + counter++); }
});
}
I think this is what you want and DEMO
$(document).ready(function() {
assign_id();
});
function assign_id() {
var numberOfDiv = $('.image-box').length;
var listOfRandomNumber = myFunction(numberOfDiv);
$('.image-box').each(function(i, obj) {
$(this).attr("id",listOfRandomNumber[i]);
});
};
//Getting List Of Number contains 1 to the number of Div which is
//14 in this case.
function myFunction(numberOfDiv ) {
for (var i = 1, ar = []; i < numberOfDiv +1 ; i++) {
ar[i] = i;
}
ar.sort(function() {
return Math.random() - 0.5;
});
return ar;
};
I would suggest to have a global array like u have for user_id and push assigned div ids into array. Then you can check before assigning random id to div. If it is assigned use different one.
Hope this helps.
Cheers.
First how you'd use my implementation:
var takeOne = makeDiminishingChoice(14);
In this case, takeOne is a function that you can call up to 14 times to get a unique random number between 1 and 14.
For example, this will output the numbers between 1 and 14 in random order:
for (var i = 0; i < 14; i++) {
console.log(takeOne());
}
And here is the implementation of makeDiminishingChoice itself:
var makeDiminishingChoice = function (howMany) {
// Generate the available choice "space" that we can select a random value from.
var pickFrom = [];
for (var i = 0; i < howMany; i++) {
pickFrom.push(i);
}
// Return a function that, when called, will return a value from the search space,
// until there are no more values left.
return function() {
if (pickFrom.length === 0) {
throw "You have requested too many values. " + howMany + " values have already been used."
}
var randomIndex = Math.floor(Math.random() * pickFrom.length);
return pickFrom.splice(randomIndex, 1)[0];
};
};
It's better to use incremental values(or something like (new Date).getTime()) if all you want to do is assign unique values. But if, for some reason, you must have randomly-picked values(only from 1-14) then use something like this to pick unique/random values
var arrayId = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
function getRandomId(array) {
var randomIndex = Math.floor(Math.random() * (array.length));
var randomId = array.splice(randomIndex, 1);
return randomId[0];
}
Now getRandomId(arrayId) would return a randomly picked value from your array and then remove that value so that it is not repeated.

How to pick random number of items randomly in an array

I want to pick random number of inputs randomly in an array of input elements..
If i use the below method i Can get one random item
jQuery.jQueryRandom = 0;
jQuery.extend(jQuery.expr[":"],
{
random: function(a, i, m, r) {
if (i == 0) {
jQuery.jQueryRandom = Math.floor(Math.random() * r.length);
};
return i == jQuery.jQueryRandom;
}
});
$("input:random").prop('id')
But I want random number i=of items to be picked randomly in an array.
You can use jQuery's .filter() method with a function:
$('div').filter(function(){
return (Math.round(Math.random()) == 1);
}).css({background: 'red'});
jsFiddle example
You currently are checking if the index is equal to the random number. Maybe it´s better to generate a random number for each input and check if its bigger then 1 or smaller. So randomize a number between 0 and 2. something like:
jQuery.extend(jQuery.expr[":"],
{
random: function(a, i, m, r) {
return Math.random() > 0.5;
}
});
alert( $("input:random").length );
Also if you get a prop from an element, it will only get it from the first one.
I'm proposing a non-jQuery answer to you question on how to get count elements randomly from an array arr
var pickRandom = function (arr, count) {
var out = [], i, pick, clone = arr.slice(0, arr.length);
for (i = 0; i < count; i ++) {
pick = Math.floor(Math.random() * clone.length);
if (clone[pick] !== undefined) {
out.push(clone[pick]);
clone.splice(pick, 1);
}
}
return out;
};
This function makes sure that the original array is preserved and handles undefined values correctly

Categories