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));
Related
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.
I am trying to find an efficient way to go through a big amount of data to determine how many units are processed at once.
So the data that I am receiving are just simple pairs:
{timestamp: *, solvetime: *}
What I need, is to see how many things are processed at each second.
To help you visualize what I mean: here is an example of data that I receive:
{{timestamp: 5, solvetime: 3},{timestamp: 7, solvetime: 5},{timestamp: 8, solvetime: 2},{timestamp: 12, solvetime: 10},{timestamp: 14, solvetime: 7}}
The chart below should help you understand how it looks in time:
https://i.stack.imgur.com/LEIhW.png
This is a simple case where the final calculation contains every second, but if the timeframe is much wider I show only 205 different times in this timeframe. E.g. if the time btw the first and the last timestamp is 20500 seconds I would calculate the usage for every second and divide the time into 205 parts - 100 seconds each and show only the second with the highest usage.
What I am doing right now is to iterate through all the pairs of input data and create a map of all the seconds, once I have it I go through this map again to find the highest usage in each time period (of 205 time periods I divide the whole time-frame in) and append it to the map of 205 timestamps.
It's working correctly, but it's very very slow and I feel like there is some better way to do it, a table might be faster but it is still not too efficient is it?
Here is the actual code that does it:
// results contain all the timestamps and solvetimes
// Timeframe of the chart
var start = Math.min.apply(Math, msgDetailsData.results.map((o) => { return o.timestamp; }))
var end = Math.max.apply(Math, msgDetailsData.results.map((o) => { return o.timestamp; }))
// map of all seconds in desired range (keys) the values are counter ofprocesses run in a given second
let mapOfSecondsInRange = new Map();
for (let i = start; i <= end; i++) {
mapOfSecondsInRange.set(i, 0);
}
// we go through every proces and add +1 to the value of each second in which the task was active
for (let element of msgDetailsData.results) {
var singleTaskStart = element.timestamp - Math.ceil(element.solveTime);
if (singleTaskStart < start) {
for (let i = singleTaskStart; i < start; i++) {
mapOfSecondsInRange.set(i, 0);
}
start = singleTaskStart;
}
for (let i = singleTaskStart; i < element.timestamp; i++) {
mapOfSecondsInRange.set(i, mapOfSecondsInRange.get(i) + 1);
}
}
// Preparation for the final map - all the seconds in the range divided into 205 parts.
const numberOfPointsOnChart = 205;
var numberOfSecondsForEachDataPoint = Math.floor((end - start) / numberOfPointsOnChart) + 1;
var leftoverSeconds = ((end - start) % numberOfPointsOnChart) + 1;
var highestUsageInGivenTimeframe = 0;
var timestampOfHighestUsage = 0;
let mapOfXXXDataPoints = new Map();
var currentElement = start;
for (let i = 0; i < numberOfPointsOnChart; i++) {
if (leftoverSeconds === 0) {
numberOfSecondsForEachDataPoint = numberOfSecondsForEachDataPoint - 1;
}
if (currentElement <= end) {
for (let j = 0; j < numberOfSecondsForEachDataPoint; j++) {
if (j === 0) {
highestUsageInGivenTimeframe = mapOfSecondsInRange.get(currentElement);
timestampOfHighestUsage = currentElement;
}
else {
if (mapOfSecondsInRange.get(currentElement) > highestUsageInGivenTimeframe) {
highestUsageInGivenTimeframe = mapOfSecondsInRange.get(currentElement);
timestampOfHighestUsage = currentElement;
}
}
currentElement = currentElement + 1;
}
mapOfXXXDataPoints.set(timestampOfHighestUsage, highestUsageInGivenTimeframe);
leftoverSeconds = leftoverSeconds - 1;
}
}
I doing some Hackerrank challange to improve my problem solving skills, so one of the challanges was about finding the total maximum numbers from an array of numbers. For example if we have 3 2 1 3 1 3 it should return 3
This is what I did :
function birthdayCakeCandles(ar) {
let total= 0
let sortedArray = ar.sort((cur,next)=>{
return cur<next
})
ar.map(item => {
if(item===sortedArray[0]) {
total ++;
}
})
return total
}
So I sorted the given array and then map through the array and check how many of the numbers are equal to the maximum number in that array and count the total.
This will pass 8/9 test cases, one of the test cases, have a array with length of 100000 and for this one it failed, this is the given data for this test case.
Really can't get it why it fails in this test, is it possible that this happened because of JavaScript which is always synchronous and single-threaded?
I tried to use Promise and async await, but hackerrank will consider the first return as the output ( Which is the Promise itself ) and it not use the resolve value as a output, so can't really test this.
Is it something wrong with my logic?
The sorting approach is too slow (O(n log n) time complexity). For algorithmic challenges on HR, it's unlikely that features somewhat particular to your language choice like promises/async are going to rescue you.
You can do this in one pass using an object to keep track of how many times you've "seen" each number and the array's maximum number, then simply index into the object to get your answer:
function birthdayCakeCandles(ar) {
let best = -Infinity;
const seen = {};
for (let i = 0; i < ar.length; i++) {
if (ar[i] > best) {
best = ar[i];
}
seen[ar[i]] = ++seen[ar[i]] || 1;
}
return seen[best];
}
Time and space complexity: O(n).
Edit:
This answer is even better, with constant space (here it is in JS):
function birthdayCakeCandles(ar) {
let best = -Infinity;
let count = 0;
for (const n of ar) {
if (n > best) {
best = n;
count = 1;
}
else if (n === best) {
count++;
}
}
return count;
}
In your case, the build in function sort is using the resource heavily. Maybe that's the reason it is failing for a space/time complexity.
BTW, This problem can be solved easily using a for loop. The idea is
Pseudocode
var maxNum = -999999; // put here the highest limit of int or what ever data type
int count = 0;
for(x in arr)
{
if (x > maxNum)
{
maxNum = x;
count = 1;
}
if(x==maxNum) count ++;
}
Here count will be the output.
The full code is
function birthdayCakeCandles(ar) {
var maxNum = -1;
var count = 0;
for(var i=0; i< ar.length; i++){
var x = ar[i];
if(x<maxNum) continue;
if(x>maxNum){
maxNum = x;
count = 1;
}
else{
count++;
}
}
return count;
}
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!
This function is meant to find the highest variable in a list of variables, which have five id letters, and one number. It works fine with all the other slots, 2, 3, 4, 5, 6, 7, 8, 9, 10, but not 1. I need another set of eyes here.
The getVer function takes the number from the id; so ImpHt1 with getVer would be 1, while getShtNm gets ImpHt.
function find_max_feat(array_input,ShtNm) {
if (String(array_input[0]).length == 1) {
var max = 0;
}
else {
var max = getVer(array_input[0]);
}
var maxver = 0
var len = array_input.length;
for (var i = 0; i < len; i++) {
if (String(array_input[i]).length > 1) {
if (getShtNm(String(array_input[i])) == ShtNm) {
if (getVer(String(array_input[i])) > maxver) {
var max = array_input[i];
var maxver = getVer(String(array_input[i]));
}
}
}
}
return max;
}
0,DmnHt1_0,AltFm1_,0,0,0,0,0,0,0
An example of the array, which is why getVer is needed.
This is for a sheet generator, to be clear, but I've been working on the entire thing for at least a few days now, maybe even a week or weeks of on and off work.
The array above is generated any time a feat is selected, and the find_max_feat array is used to find the highest version in a group; it operates off of an infinite loop since nothing else I did could get it to work the way I wanted it to.
function checkFeats() {
updateFeatsel();
t=setTimeout("checkFeats()",1000);
}
function updateFeatsel() {
curselarray = new Array();
var selinc = 1;
while (selinc <= 10) {
var selincar = selinc - 1;
var selid = document.getElementById(String('ftlst' + selinc));
if (getVer(selid.options[selid.selectedIndex].title)) {
curselarray[selincar] = selid.options[selid.selectedIndex].title;
}
else {
curselarray[selincar] = 0;
}
selinc++;
}
document.getElementById('debug1').innerHTML = curselarray.valueOf();
featSelch('hlthm','ImpHt',healthom);
featSelch('strdmgm','ImpPd',Strpdom);
featSelch('strwhtm','ImpLi',Strwhtom);
featSelch('strsltm','EnhIt',StrSltom);
featSelch('endsurm','ImpEn',EndSurom);
featSelch('endsokm','ImpDf',EndSokom);
featSelch('intelmpm','ImpMg',Intelmom);
featSelch('willsokm','ImpMs',Willsokom);
featSelch('luckrllm','ImpLu',Lukrllom);
featSelch('luckpntm','EnhLu',Lukpntom);
featSelch('hlthbn','DmnHt',0);
featSelch('strbn','SupSt',0);
featSelch('luckbn','DmnLu',0);
featSelch('endbn','Armor',0)
document.getElementById('debug2').innerHTML = find_max_feat(curselarray,'DmnHt');
updateAmounts();
}
function featSelch(sid,fshtnm,defval) {
return document.getElementById(sid).innerHTML = getFeatvalue(fshtnm,defval);
}
That is because you are initialising max using getVer(array_input[0]) instead of array_input[0]. If the first item is the highest and has the version number zero, the initial value is used.
Change this:
var max = getVer(array_input[0]);
into:
var max = array_input[0];