Run javascript in Bubble with loops to compare JSON API data - javascript

I'm building an app using Bubble and I have the Toolbox plugin which enables me to use the "Run javascript" step in a Workflow.
From the following NASA API url, I'm planning to read and analyze 20 years of data with values for every month (240 values):
https://power.larc.nasa.gov/api/temporal/monthly/point?parameters=ALLSKY_SFC_SW_DNI&community=RE&longitude=48.0000&latitude=27.0000&format=JSON&start=2001&end=2020
Below is what I am trying to accomplish (with missing pieces of code):
Read in the json data from the API and parse it into an Javascript object.
const response = await fetch(api_url);
const data = await response.json();
Loop through the objects values of interest. Inside the loop calculate the average values for each month. January, February, March, etc.
for(const entry of Object.entries(data.properties.parameters.ALLSKY_SFC_SW_DNI)){}
Store the above 12 average values to the Bubble database.
Loop through the 12 average values to find the two months with highest and lowest value.
Store the above High/low months values to the Bubble database. For example January as lowest average value.
I have successfully learned how to do some basic Javascript operations in Bubble. Reading single values from the API, display them on the page, and store them in the Bubble database. But, I do not know how to read the values in to an JS array, and make the necessary loops and comparisons.
I found this older question but it's about a txt file instead of json in my case.
Get an array of values using fetch api javascript
Appreciate any advice and directions. Thanks

We know the data exists # data.properties.parameter.ALLSKY_SFC_SW_DNI
The year and month are combined so the month must be extracted. Here is one simple way. (note I chose to remove the leading zero for a reason)
/* Given '202001' returns '1', given 202012' returns '12' */
function extractMonth(yearmonth){
let month = yearmonth.substring(yearmonth.length-2);
return month.startsWith('0')? month[month.length-1] : month;
}
If you store the data in a simple Array, the indexes can serve as the Month number. So index 1 will represent January. In order to do that the first element (index 0) will be set to null.
The reasons I took this approach is because the data structure is simple and it makes finding the lowest and highest values quite easy.
Now it is as easy as populating each Array element with the average value inside the loop. Each iteration of the loop updates the average for the month.
for(let [yearmonth, measurement] of Object.entries(data.properties.parameter.ALLSKY_SFC_SW_DNI)){
const month = extractMonth(yearmonth);
if(!tempsByMonth[month]) tempsByMonth[month] = 0;
tempsByMonth[month] = (tempsByMonth[month] + measurement)/2;
}
It looks like this:
const data = {"type":"Feature","geometry":{"type":"Point","coordinates":[48.0,27.0,191.14]},"properties":{"parameter":{"ALLSKY_SFC_SW_DNI":{"200101":5.65,"200102":4.88,"200103":5.28,"200104":5.05,"200105":4.84,"200106":5.21,"200107":5.44,"200108":6.07,"200109":5.72,"200110":5.3,"200111":4.52,"200112":3.03,"200113":5.08,"200201":4.09,"200202":4.89,"200203":4.49,"200204":4.19,"200205":5.17,"200206":6.12,"200207":6.15,"200208":5.64,"200209":5.92,"200210":5.5,"200211":4.57,"200212":3.26,"200213":5.0,"200301":4.55,"200302":3.9,"200303":4.09,"200304":4.29,"200305":4.39,"200306":6.1,"200307":5.29,"200308":6.14,"200309":6.3,"200310":5.28,"200311":3.93,"200312":3.39,"200313":4.81,"200401":3.51,"200402":4.45,"200403":5.37,"200404":4.01,"200405":5.39,"200406":6.4,"200407":6.42,"200408":6.11,"200409":6.21,"200410":5.86,"200411":3.66,"200412":3.89,"200413":5.11,"200501":4.05,"200502":4.03,"200503":4.52,"200504":4.49,"200505":5.14,"200506":5.33,"200507":5.06,"200508":5.85,"200509":6.08,"200510":5.45,"200511":3.89,"200512":4.06,"200513":4.83,"200601":3.65,"200602":3.43,"200603":4.85,"200604":4.0,"200605":4.57,"200606":6.08,"200607":5.01,"200608":5.5,"200609":6.32,"200610":4.81,"200611":4.08,"200612":3.99,"200613":4.7,"200701":4.27,"200702":4.56,"200703":4.39,"200704":3.51,"200705":4.75,"200706":5.4,"200707":5.18,"200708":5.77,"200709":5.88,"200710":5.43,"200711":4.7,"200712":3.76,"200713":4.8,"200801":3.26,"200802":4.12,"200803":4.64,"200804":3.77,"200805":4.03,"200806":4.52,"200807":4.84,"200808":5.3,"200809":4.71,"200810":4.76,"200811":3.97,"200812":5.08,"200813":4.42,"200901":4.08,"200902":3.23,"200903":3.68,"200904":3.66,"200905":4.57,"200906":4.9,"200907":4.23,"200908":4.73,"200909":5.03,"200910":4.57,"200911":4.15,"200912":3.89,"200913":4.23,"201001":4.73,"201002":3.99,"201003":4.51,"201004":3.52,"201005":4.36,"201006":5.12,"201007":4.8,"201008":5.17,"201009":5.28,"201010":5.14,"201011":5.39,"201012":4.65,"201013":4.73,"201101":3.56,"201102":3.47,"201103":4.1,"201104":2.83,"201105":4.52,"201106":4.37,"201107":4.63,"201108":5.31,"201109":5.53,"201110":4.8,"201111":3.71,"201112":4.81,"201113":4.31,"201201":4.19,"201202":3.51,"201203":3.36,"201204":3.34,"201205":3.68,"201206":4.9,"201207":5.33,"201208":5.18,"201209":5.7,"201210":4.84,"201211":4.43,"201212":3.52,"201213":4.33,"201301":3.44,"201302":4.4,"201303":4.03,"201304":3.79,"201305":4.96,"201306":4.94,"201307":4.97,"201308":5.45,"201309":5.39,"201310":5.11,"201311":4.14,"201312":4.38,"201313":4.58,"201401":3.86,"201402":4.49,"201403":4.28,"201404":4.19,"201405":4.78,"201406":5.71,"201407":5.4,"201408":5.37,"201409":5.82,"201410":4.61,"201411":4.21,"201412":4.66,"201413":4.78,"201501":4.24,"201502":3.73,"201503":4.01,"201504":3.79,"201505":4.18,"201506":5.02,"201507":4.7,"201508":5.67,"201509":4.94,"201510":4.17,"201511":3.77,"201512":3.45,"201513":4.31,"201601":4.19,"201602":5.15,"201603":3.77,"201604":4.8,"201605":5.0,"201606":5.73,"201607":4.92,"201608":5.69,"201609":5.49,"201610":5.5,"201611":4.16,"201612":3.46,"201613":4.82,"201701":3.8,"201702":4.54,"201703":3.76,"201704":4.32,"201705":4.89,"201706":7.03,"201707":6.06,"201708":6.37,"201709":6.41,"201710":5.69,"201711":4.48,"201712":4.72,"201713":5.17,"201801":4.6,"201802":4.12,"201803":5.46,"201804":3.69,"201805":5.18,"201806":5.6,"201807":5.71,"201808":5.88,"201809":6.16,"201810":4.48,"201811":3.29,"201812":3.9,"201813":4.85,"201901":2.72,"201902":4.73,"201903":4.92,"201904":4.62,"201905":5.73,"201906":7.4,"201907":5.76,"201908":6.02,"201909":6.66,"201910":5.02,"201911":4.33,"201912":4.65,"201913":5.21,"202001":4.6,"202002":4.85,"202003":5.12,"202004":4.48,"202005":6.14,"202006":6.77,"202007":6.52,"202008":6.37,"202009":6.58,"202010":6.86,"202011":4.21,"202012":3.89,"202013":5.54}}},"header":{"title":"NASA/POWER CERES/MERRA2 Native Resolution Monthly and Annual","api":{"version":"v2.2.12","name":"POWER Monthly and Annual API"},"fill_value":-999.0,"start":"20010101","end":"20201231"},"messages":[],"parameters":{"ALLSKY_SFC_SW_DNI":{"units":"kW-hr/m^2/day","longname":"All Sky Surface Shortwave Downward Direct Normal Irradiance"}},"times":{"data":0.745,"process":0.02}};
const tempsByMonth = [null];
for(let [yearmonth, measurement] of Object.entries(data.properties.parameter.ALLSKY_SFC_SW_DNI)){
const month = extractMonth(yearmonth);
if(!tempsByMonth[month]) tempsByMonth[month] = 0;
tempsByMonth[month] = (tempsByMonth[month] + measurement)/2;
}
function extractMonth(yearmonth){
let month = yearmonth.substring(yearmonth.length-2);
return month.startsWith('0')? month[month.length-1] : month;
}
console.log(tempsByMonth);
Finally, to find the lowest and highest average, you'll use Math.max and Math.min. Note that for the lowest average the null value was filtered out of the list:
console.log('Month with greatest value: ', tempsByMonth.indexOf(Math.max(...tempsByMonth)));
console.log('Month with lowest value: ', tempsByMonth.indexOf(Math.min(...tempsByMonth.filter(v=>v!==null))));
And here is the final result in action:
const data = {"type":"Feature","geometry":{"type":"Point","coordinates":[48.0,27.0,191.14]},"properties":{"parameter":{"ALLSKY_SFC_SW_DNI":{"200101":5.65,"200102":4.88,"200103":5.28,"200104":5.05,"200105":4.84,"200106":5.21,"200107":5.44,"200108":6.07,"200109":5.72,"200110":5.3,"200111":4.52,"200112":3.03,"200113":5.08,"200201":4.09,"200202":4.89,"200203":4.49,"200204":4.19,"200205":5.17,"200206":6.12,"200207":6.15,"200208":5.64,"200209":5.92,"200210":5.5,"200211":4.57,"200212":3.26,"200213":5.0,"200301":4.55,"200302":3.9,"200303":4.09,"200304":4.29,"200305":4.39,"200306":6.1,"200307":5.29,"200308":6.14,"200309":6.3,"200310":5.28,"200311":3.93,"200312":3.39,"200313":4.81,"200401":3.51,"200402":4.45,"200403":5.37,"200404":4.01,"200405":5.39,"200406":6.4,"200407":6.42,"200408":6.11,"200409":6.21,"200410":5.86,"200411":3.66,"200412":3.89,"200413":5.11,"200501":4.05,"200502":4.03,"200503":4.52,"200504":4.49,"200505":5.14,"200506":5.33,"200507":5.06,"200508":5.85,"200509":6.08,"200510":5.45,"200511":3.89,"200512":4.06,"200513":4.83,"200601":3.65,"200602":3.43,"200603":4.85,"200604":4.0,"200605":4.57,"200606":6.08,"200607":5.01,"200608":5.5,"200609":6.32,"200610":4.81,"200611":4.08,"200612":3.99,"200613":4.7,"200701":4.27,"200702":4.56,"200703":4.39,"200704":3.51,"200705":4.75,"200706":5.4,"200707":5.18,"200708":5.77,"200709":5.88,"200710":5.43,"200711":4.7,"200712":3.76,"200713":4.8,"200801":3.26,"200802":4.12,"200803":4.64,"200804":3.77,"200805":4.03,"200806":4.52,"200807":4.84,"200808":5.3,"200809":4.71,"200810":4.76,"200811":3.97,"200812":5.08,"200813":4.42,"200901":4.08,"200902":3.23,"200903":3.68,"200904":3.66,"200905":4.57,"200906":4.9,"200907":4.23,"200908":4.73,"200909":5.03,"200910":4.57,"200911":4.15,"200912":3.89,"200913":4.23,"201001":4.73,"201002":3.99,"201003":4.51,"201004":3.52,"201005":4.36,"201006":5.12,"201007":4.8,"201008":5.17,"201009":5.28,"201010":5.14,"201011":5.39,"201012":4.65,"201013":4.73,"201101":3.56,"201102":3.47,"201103":4.1,"201104":2.83,"201105":4.52,"201106":4.37,"201107":4.63,"201108":5.31,"201109":5.53,"201110":4.8,"201111":3.71,"201112":4.81,"201113":4.31,"201201":4.19,"201202":3.51,"201203":3.36,"201204":3.34,"201205":3.68,"201206":4.9,"201207":5.33,"201208":5.18,"201209":5.7,"201210":4.84,"201211":4.43,"201212":3.52,"201213":4.33,"201301":3.44,"201302":4.4,"201303":4.03,"201304":3.79,"201305":4.96,"201306":4.94,"201307":4.97,"201308":5.45,"201309":5.39,"201310":5.11,"201311":4.14,"201312":4.38,"201313":4.58,"201401":3.86,"201402":4.49,"201403":4.28,"201404":4.19,"201405":4.78,"201406":5.71,"201407":5.4,"201408":5.37,"201409":5.82,"201410":4.61,"201411":4.21,"201412":4.66,"201413":4.78,"201501":4.24,"201502":3.73,"201503":4.01,"201504":3.79,"201505":4.18,"201506":5.02,"201507":4.7,"201508":5.67,"201509":4.94,"201510":4.17,"201511":3.77,"201512":3.45,"201513":4.31,"201601":4.19,"201602":5.15,"201603":3.77,"201604":4.8,"201605":5.0,"201606":5.73,"201607":4.92,"201608":5.69,"201609":5.49,"201610":5.5,"201611":4.16,"201612":3.46,"201613":4.82,"201701":3.8,"201702":4.54,"201703":3.76,"201704":4.32,"201705":4.89,"201706":7.03,"201707":6.06,"201708":6.37,"201709":6.41,"201710":5.69,"201711":4.48,"201712":4.72,"201713":5.17,"201801":4.6,"201802":4.12,"201803":5.46,"201804":3.69,"201805":5.18,"201806":5.6,"201807":5.71,"201808":5.88,"201809":6.16,"201810":4.48,"201811":3.29,"201812":3.9,"201813":4.85,"201901":2.72,"201902":4.73,"201903":4.92,"201904":4.62,"201905":5.73,"201906":7.4,"201907":5.76,"201908":6.02,"201909":6.66,"201910":5.02,"201911":4.33,"201912":4.65,"201913":5.21,"202001":4.6,"202002":4.85,"202003":5.12,"202004":4.48,"202005":6.14,"202006":6.77,"202007":6.52,"202008":6.37,"202009":6.58,"202010":6.86,"202011":4.21,"202012":3.89,"202013":5.54}}},"header":{"title":"NASA/POWER CERES/MERRA2 Native Resolution Monthly and Annual","api":{"version":"v2.2.12","name":"POWER Monthly and Annual API"},"fill_value":-999.0,"start":"20010101","end":"20201231"},"messages":[],"parameters":{"ALLSKY_SFC_SW_DNI":{"units":"kW-hr/m^2/day","longname":"All Sky Surface Shortwave Downward Direct Normal Irradiance"}},"times":{"data":0.745,"process":0.02}};
const tempsByMonth = [null];
for(let [yearmonth, measurement] of Object.entries(data.properties.parameter.ALLSKY_SFC_SW_DNI)){
const month = extractMonth(yearmonth);
if(!tempsByMonth[month]) tempsByMonth[month] = 0;
tempsByMonth[month] = (tempsByMonth[month] + measurement)/2;
}
function extractMonth(yearmonth){
let month = yearmonth.substring(yearmonth.length-2);
return month.startsWith('0')? month[month.length-1] : month;
}
console.log(tempsByMonth);
console.log('Month with greatest value: ', tempsByMonth.indexOf(Math.max(...tempsByMonth)));
console.log('Month with lowest value: ', tempsByMonth.indexOf(Math.min(...tempsByMonth.filter(v=>v!==null))));

Given what you showed me, I just fixed a couple lines and you say that you understand the bubble database end of whatever you're doing so here's how to get your data from the api, but as for the months.. something strange, apparently there are 13 data points per year ;-;
Nonetheless, I'd just have a list of 13 averages instead of 12 due to the data I'm getting
let api_url="https://power.larc.nasa.gov/api/temporal/monthly/point?parameters=ALLSKY_SFC_SW_DNI&community=RE&longitude=48.0000&latitude=27.0000&format=JSON&start=2001&end=2020"
var list={} //will store data points on each month "number"
let response = await fetch(api_url)
let parseData = await response.json()
let interest=parseData.properties.parameter.ALLSKY_SFC_SW_DNI
for(const entry of Object.entries(interest)){
let [key,value]=entry //value is value xD
key=key.substring(4,6) //key becomes month "number"
list[key]=list[key]||{average:0,count:0}
list[key].count++; list[key].average+=value
}
Object.entries(list).forEach(([_,month])=>{
month.average/=month.count
})
console.log(list[12]) //sample of a month data
console.log(list) //full thing
Here's what it looks like

Related

Using Jscript Add ONLY the integers in a 2D array. Then working out the difference between each month of the same array to get the average difference

Javavscript help (array at bottom)
I have a 2D array which holds a date of month in one column (a string) and an integer next to it i.e. array= ['Oct 2020' , 23456], ['Nov 2020' , 34567], ['Dec 2020' , -4567]...etc I have to work out:
total amount of months in this array (which I've managed),
the total of all the profit/loss form the integers (which I've managed alt methods welcome it'll help me learn more),
the average difference of profit/loss between each month's by calculating the difference between each month first then adding and dividing by total number of months,
the month with the biggest profit in the array
the month with biggest lost in the array.
I'm able to provide the code. I think I understand how to do this in a 1D array but with a 2D array the results didn't work. Please could I have some help and a walk through of your answer. I don't mind how its worked out but if you could have an example with the reduce function in your answer and an answer without that would be great if not no problem.
var finances = [
['Jan-2010', 867884],
['Feb-2010', 984655],
['Mar-2010', 322013],
['Apr-2010', -69417],
['May-2010', 310503],
['Jun-2010', 522857],
['Jul-2010', 1033096],
['Aug-2010', 604885],
['Sep-2010', -216386],
['Oct-2010', 477532],
['Nov-2010', 893810],
['Dec-2010', -80353],
['Jan-2011', 779806],
['Feb-2011', -335203],
['Mar-2011', 697845],
['Apr-2011', 793163],
['May-2011', 485070],
['Jun-2011', 584122],
['Jul-2011', 62729],
['Aug-2011', 668179],
['Sep-2011', 899906],
['Oct-2011', 834719],
['Nov-2011', 132003],
['Dec-2011', 309978],
['Jan-2012', -755566],
['Feb-2012', 1170593],
['Mar-2012', 252788],
['Apr-2012', 1151518],
['May-2012', 817256],
['Jun-2012', 570757],
['Jul-2012', 506702],
['Aug-2012', -1022534],
['Sep-2012', 475062],
['Oct-2012', 779976],
['Nov-2012', 144175],
['Dec-2012', 542494],
['Jan-2013', 359333],
['Feb-2013', 321469],
['Mar-2013', 67780],
['Apr-2013', 471435],
['May-2013', 565603],
['Jun-2013', 872480],
['Jul-2013', 789480],
['Aug-2013', 999942],
['Sep-2013', -1196225],
['Oct-2013', 268997],
['Nov-2013', -687986],
['Dec-2013', 1150461],
['Jan-2014', 682458],
['Feb-2014', 617856],
['Mar-2014', 824098],
['Apr-2014', 581943],
['May-2014', 132864],
['Jun-2014', 448062],
['Jul-2014', 689161],
['Aug-2014', 800701],
['Sep-2014', 1166643],
['Oct-2014', 947333],
['Nov-2014', 578668],
['Dec-2014', 988505],
['Jan-2015', 1139715],
['Feb-2015', 1029471],
['Mar-2015', 687533],
['Apr-2015', -524626],
['May-2015', 158620],
['Jun-2015', 87795],
['Jul-2015', 423389],
['Aug-2015', 840723],
['Sep-2015', 568529],
['Oct-2015', 332067],
['Nov-2015', 989499],
['Dec-2015', 778237],
['Jan-2016', 650000],
['Feb-2016', -1100387],
['Mar-2016', -174946],
['Apr-2016', 757143],
['May-2016', 445709],
['Jun-2016', 712961],
['Jul-2016', -1163797],
['Aug-2016', 569899],
['Sep-2016', 768450],
['Oct-2016', 102685],
['Nov-2016', 795914],
['Dec-2016', 60988],
['Jan-2017', 138230],
['Feb-2017', 671099]
];
code for how many months:
let monthsTotal = finances.length;
console.log("Total months: ", monthsTotal);
my first attempt to try and find the total profits/losses (i.e. sum of all integers). It just printed out the array in a messy form
const netTotal =finances.reduce((sum, curVal) => sum + curVal);
console.log("Total Profits/Loses: ", netTotal);
my second attempt actually works to find the sum which i have called netTotal
let netTotal = finances.map(function(v) { return v[1] }) // second value of each
.reduce(function(a,b) { return a + b }); // sum
console.log('Total:', netTotal)
so just the the last 3 bullet points technically
In the netTotal calculation, we don't need the extra trip through the array to get the integer value. Just pull it out with [1] as you go:
let netTotal = finances.reduce((a,b) => a + b[1]);
Getting the differences between months is simple if you handle the special case of the first month not having a prior month to compare to.
let diffs = finances.map((el, index) => {
// on the first one, assume the prior value is 0
let priorValue = index === 0 ? 0 : finances[index-1][1]; // edit
return el[1] - priorValue;
})
let totalDiffs = diffs.reduce((a,b) => a + b); // simple sum
let averageDiffs = totalDiffs / finances.length
Max and min are a little extra work because the calculation should track the months also. Here's a way to do it where the max/min thing being tracked is the nested array itself.
let maxArr = ['', -Number.MAX_VALUE] // everything is bigger than this
let minArr = ['', Number.MAX_VALUE] // everything is smaller than this
for (let i=0; i<finances.length; i++) {
let arr = finances[i];
if (arr[1] > maxArr[1]) maxArr = arr;
if (arr[1] < minArr[1]) minArr = arr;
}

Datetime array to array with dates, get corresponding time afterwards

Specific situation.. I'm having an array filled with datetimes I pull in via an api.
Users should be able to select a date from a datepicker (only showing dates available in the array) and afterwards see the corresponding time.
So what I've done..
The original array is obtained via php, so before starting to populate the datepicker with possible dates I create an extra array with dates only.
Since I maintain the key's it's possible to put these 2 arrays next to eachother.
Array looks as following:
["8-8-2017,07:00", "26-8-2017,07:00"];
So far so good...
After a user picks a date I trigger this to be able to start digging for the time corresponding that date.
Now it's getting messy...
$('#datepick').datepicker().on("input change", function(e) {
$("#uur").text('');
var selecteddate = e.target.value;
var searchArr = datesArray;
var ind = searchArr.indexOf(selecteddate.toString());
var result = datesArray.filter(function(item) {
return typeof item == 'string' && item.indexOf(selecteddate.toString()) > -1;
});
var afterComma = result.toString().substr(result.toString().indexOf(",") + 1);
var final = afterComma.replace(":", "u");
$("#uur").text("De warming up party gaat van start rond " + final);
});
The result is that this only works on the last element of the array.
Because I'm splitting based on the comma's. Now I know the easiest way to work arround this would be to change the , that's seperating date and time in another symbol but still I'm wondering why this couldn't be easier.
You convert whole array to string every time. You should change following code:
var afterComma = result.toString().substr(result.toString().indexOf(",") + 1);
To this;
var afterComma = item.toString().substr(item.toString().indexOf(",") + 1);
Edit:
I also missed the loop above
//for every item in result, afterComma will refer to related minute string
for (var item in result) {
var afterComma = item.toString().substr(item.toString().indexOf(",") + 1);
// Do rest here
}

How to ask an API for many values (of the past 14 days) via iterative timestamp manipulation?

I want to calculate the RSI - (Relative Strength Index)(Last 14 Days) for the Bitcoin-Price. The necessary data for the past 14 days comes from an API. The result should be stored in a variable for further processing. I tried different methods, but the result wasn´t working. I think i don´t found the right way to solve this. I was not able to create a complete and working result, so I am here to ask my first question on Stack Overflow.
How can I ask the API for the course of the past 14 days via
iterative timestamp manipulation?
Timestamp now -1day/-2days.... (&ts=xxxxxxxxxxxx) - example: (ts=1452680400)
https://min-api.cryptocompare.com/data/pricehistorical?fsym=BTC&tsyms=USD&ts=1452680400
How can i put the values from the API in an array?:
var closePrices= {
var : 'text',
array: [BTCDay1, BTCDay2, BTCDay3, BTCDay4, BTCDay5, BTCDay6, BTCDay7, BTCDay8, BTCDay9, BTCDay10, BTCDay11, BTCDay12, BTCDay13, BTCDay14]
};
Then I want to put the array in this calculation formular:
public static double CalculateRsi(IEnumerable<double> closePrices)
{
var prices = closePrices as double[] ?? closePrices.ToArray();
double sumGain = 0;
double sumLoss = 0;
for (int i = 1; i < prices.Length; i++)
{
var difference = prices[i] - prices[i - 1];
if (difference >= 0)
{
sumGain += difference;
}
else
{
sumLoss -= difference;
}
}
if (sumGain == 0) return 0;
if (Math.Abs(sumLoss) < Tolerance) return 100;
var relativeStrength = sumGain / sumLoss;
return 100.0 - (100.0 / (1 + relativeStrength));
}
Several quite broad questions (it usually works better if you post some of the code you already tried to write yourself)... But fun ones to solve.
Assuming we can use es6 syntax, Promises and fetch (if you can't, look up how to polyfill & transpile).
Creating an array of timestamps
To get a timestamp for the current date, you write Date.now(). To change this time stamp to a day n days ago, we decrease it by the number of milliseconds in a day:
const timeStampForDaysAgo =
nrOfDays => Date.now() - 1000 * 60 * 60 * 24 * nrOfDays;
// e.g.: yesterday
const yesterday = timeStampForDaysAgo(1);
Now, if we fill an array of the integers 0...14, we can use map to create an array of timestamps!
const lastTwoWeeks = Array.from(Array(14), (_, i) => timeStampForDaysAgo(i))
Doing the requests
In modern browsers, you can use fetch to do requests. We'll need a list of URLs though, not just timestamps. To make the URLs, we again use map:
const urls = lastTwoWeeks.map(ts => `https://your-url.com?ts=${ts}`);
Now that we have URLs, we can create our requests (again, using map):
const btcRequests = urls.map(url => fetch(url).then(r => r.json()));
Calculating the result
We can't start calculating the result until all requests have finished. That's where Promise.all comes in:
Promise.all(btcRequests).then(calcRSI);
This makes sure we only call calcRSI until all requests have finished.
Because the API returns objects of { BTC: { USD: Number } }, we'll have to extract the numbers before we can do our math.
Promise
.all(btcRequests)
.then(responses => responses.map(obj => obj.BTC.USD))
Now that we have an array of numbers, we can finally call the calculate function you gave in C# code. Of course, you'll have to translate it to javascript first.
const calcRSI = (arrayOfValues) => /* do math, return value */
Promise
.all(btcRequests)
.then(responses => responses.map(obj => obj.BTC.USD))
.then(calcRSI)
.then(rsi => console.log("The RSI for the last 14 days, is:", rsi);
See the code in action in this fiddle (make sure to open the console)

dc.js and crossfilter reduce average counts per day of week

I am struggle to get my crossfilter groups set up right. Maybe someone can drop a hint!
My datastructure looks more or less this way:
{datetime: "2014-01-01 20:00:00", id:1}
{datetime: "2014-01-01 22:21:08", id:2}
{datetime: "2014-01-02 12:00:23", id:3} etc...
The dimension is on datetime to return the day of week:
var weekdayDimension = ndx.dimension(function(d) {
return new Date(d.datetime).getDay();
});
Now I have problems with the grouping. I want the average count of events per weekday. So far I have (of course no correct)
var weekdayAvgGroup = weekdayDimension.group(function (d) {
return d;
});
I think I do not understand what that grouping is doing exactly...
My goal is to have some chart like:
Monday => Average 40.3 Events
Tuesday => Average 35.4 Events
I created a JSFiddle please take a look.
Can anybody drop a hint please?
UPDATE:
After additional thinking I could create a dimension on the Date. All I would have to do is to know the number of days selected in order to calculate the
(total amount of events selected/number of selected days)
So I would need to count the number of groups on the date dimension. But haven't found the solution on this one either.
Thank you
The annotated stock example shows how to do averages:
http://dc-js.github.io/dc.js/docs/stock.html
Basically you will use a custom reduce function, maintain a count and a sum, and divide the sum by the count (if the count is greater than zero) to get an average.
Reductio also makes this pretty easy:
https://github.com/esjewett/reductio
EDIT: Looking back on this, I notice you mean the average of the aggregated counts, across the unique dates for each day of the week.
I know it's too late, but since we get a fair number of these "second-level aggregation" questions, I thought I'd answer this one, in case it helps someone else.
So, our results should bin the data on the day of the week, so we'll set up our dimension and group accordingly:
// dimension on day of week
var dim1 = ndx.dimension(function(d) {
return d[0].getDay();
});
// group on day of week
var grp1 = dim1.group().reduce(
... // what goes here?
);
But how do we do the second-level aggregation? Already crossfilter is going to give all the entries for each day of the week, efficiently. What we need to do is count the entries per unique date.
We can use d3.map for this. We'll first use d3.time.day to remove the time-of-day info, then use .getTime() to get an integer we can index on. Then d3.map creates the "all Mondays", "all Tuesdays" bins:
var grp1 = dim1.group().reduce(
function(p, v) { // add
var day = d3.time.day(v[0]).getTime();
p.map.set(day, p.map.has(day) ? p.map.get(day) + 1 : 1);
p.avg = average_map(p.map);
return p;
},
function(p, v) { // remove
var day = d3.time.day(v[0]).getTime();
p.map.set(day, p.map.has(day) ? p.map.get(day) - 1 : 0);
p.avg = average_map(p.map);
return p;
},
function() { // init
return {map: d3.map(), avg: 0};
}
);
Finally, we'll compute the average of all bins in the d3.map with this function:
function average_map(m) {
var sum = 0;
m.forEach(function(k, v) {
sum += v;
});
return m.size() ? sum / m.size() : 0;
}
It might not be so efficient to walk the d3.map every time a day is added, so the call to average_map could be moved into the valueAccessor we'll use in the chart. I'll leave that as an exercise.
Here is a fiddle demonstrating the technique:
http://jsfiddle.net/gordonwoodhull/0woyhg3n/11/
And applied to the original fiddle:
http://jsfiddle.net/gordonwoodhull/pkh03azq/6/

How do I loop through objects and categorize by timestamps in Javascript?

I have an array of objects that have a keys called timestamp and motion. motion contains a value and timestamp contains a unix timestamp. I want to iterate over a number of the objects and find what "time of day" period they correspond to, I then want to total up the motion values for that given time of day and save the entire thing in an array of arrays. I want the duration to be changeable.
Let's say these are my objects;
{
timestamp: 1397160634,
motion: 2,
id: '534771d8c311731e21c75c9f'
},
{
timestamp: 1397160634,
motion: 3,
id: '534771d8c311731e21c75c9f'
}
Now I create my results array
var sampleDuration = 60; // Min
var minutesInDay = 1440;
var samplesPerDay = minutesInDay/sampleDuration;
var finalResultItem = []
for (var i = 0; i < samplesPerDay; i++) {
var IndividualresultArray = []
IndividualresultArray.push(60*i);
IndividualresultArray.push(0);
finalResultItem.push(IndividualresultArray);
}
I now have an array of arrays with each subarray's first item being a number (corresponding to a minute stamp) and the second value being zero.
I would now like to loop through all my objects and increment the second value (motion) based on the time of day range that is in the timestamp
_forEach(objects, function (object) {
{
// grab the timestamp
// figure out which minute range it coresponds to
// increment the array value that corresponds to the minute stamp
// rinse and repeat
}
this is where I go blank, I need the end result to look something like this
[[30, 5],[60, 20],[90, 5],[120, 0] .........]
or it could even look like this
[[000002400, 5],[000003000, 20],[000003600, 5],[000004200, 0] .........]
where the first value is a timestamp that ignores the year, month, and day, and only considers the time of day.
I have considered using moment.js in some capacity but I'm not sure how. Any help with this problem would be great.
I created a jsFiddle for you. The motion increment logic should look like (I'm using jQuery here but you get the point)
// Loop through and increment motion
$.each(objs, function (idx, obj) {
var date = new Date(obj.timestamp * 1000); // Convert to milliseconds
var minutesInDay = date.getUTCHours() * 60 + date.getUTCMinutes(); // Remove UTC for local time!
var minuteRange = Math.floor(minutesInDay / sampleDuration);
finalResultItem[minuteRange][1] += obj.motion;
});
EDIT: Removed some discussion after your edit. I also used more generic logic based on sampleDuration.
This should do it:
_forEach(objects, function (object) {
var date = new Date(objec.timestamp*1000);
var minuteOfDay = date.getUTCHours()*60+date.getUTCMinutes();
finalResultItem[minuteOfDay][1] += object.motion;
})
For a variable sample rate, employ a secondOfDay and divide that by your sampleDuration, then floor it to get your array index.

Categories