Dynamic multidemension Array with Date keys in Javascript - javascript

how do I loop the newly created dynamic array by key and sub array.
var days = new Array();
$.each(json_object, function(r, row) {
var online_date = new Date(row.date_field * 1000);
var day_key = '' + online_date.getFullYear() + '' + (online_date.getMonth()+1) + '' + online_date.getDate() + '';
if(!days[day_key]) {
days[day_key] = [];
}
days[day_key][r] = row;
});
console.log('days.length');
console.log(Object.keys(days).length);
// 3 days woth of data..
// 20, 30 records each day...
for(var d = 0; d < Object.keys(days).length; d++) {
var day = days[d];
// day is undefined
console.log(day);
// I want KEY for 'day_key' and the data rows...
}
I'm using jQuery or basic JS.
If I filter or sort the array I loose my Keys, which I need as I want to graph out per day.
Thanks.

You should declare days as an object, not an array:
var days = {};
$.each(json_object, function(r, row) {
var online_date = new Date(row.date_field * 1000);
var day_key = '' + online_date.getFullYear() + '' + (online_date.getMonth()+1) + '' + online_date.getDate() + '';
if(!days[day_key]) {
days[day_key] = []; // Define an array for the rows to go into
}
days[day_key].push(row); // Add the row to the array
});
console.log('days.length');
console.log(Object.keys(days).length);
// 3 days worth of data..
// 20, 30 records each day...
Object.keys(days).forEach(day => {
console.log(day);
// I want KEY for 'day_key' and the data rows...
// The array of rows for the day are available
console.log(`Rows for ${day}: `,days[day])
})
I think this will do what you want

Related

Calculation with Array values not working in JS

in my current project I need a Pie Chart in which calculated values should be displayed.
I have now five values as array, which I need to add, so that I have the desired value.
But now I am a bit confused, because no matter if I convert the arrays to sting, or use them directly in the addition, they are always lined up and not added.
What am I missing here?
In a subtraction directly after the calculation works, but here I still have a date value (number of days in the month) in the calculation.
Why does this calculation work?
My Problem
For example I get here "02400" as result and not "6".
var training = training_intervall + training_longrun + training_speedwork + training_stabilisation + training_competition;
My JS function:
function userDiaryMonthTrainingStats(user_id) {
$.ajax({
url: "../diary/includes/training/diary-training-monthly-training-stats.php?user_id=" + user_id,
type: "GET",
success: function(monthly_training_stats) {
var training_intervall = [];
var training_longrun = [];
var training_speedwork = [];
var training_stabilisation = [];
var training_competition = [];
var training_injury = [];
for(var i in monthly_training_stats) {
training_intervall.push(monthly_training_stats[i].training_intervall),
training_longrun.push(monthly_training_stats[i].training_longrun),
training_speedwork.push(monthly_training_stats[i].training_speedwork),
training_stabilisation.push(monthly_training_stats[i].training_stabilisation),
training_competition.push(monthly_training_stats[i].training_competition),
training_injury.push(monthly_training_stats[i].training_injury)
}
var date = new Date();
var month = date.getMonth() + 1;
var year = date.getFullYear();
daysInMonth = new Date(year, month, 0).getDate();
training_intervall = training_intervall.toString();
training_longrun = training_longrun.toString();
training_speedwork = training_speedwork.toString();
training_stabilisation = training_stabilisation.toString();
training_competition = training_competition.toString();
var training = training_intervall + training_longrun + training_speedwork + training_stabilisation + training_competition;
var training_free = daysInMonth - training_intervall - training_longrun - training_speedwork - training_stabilisation - training_competition - training_injury;
var userMonthlyTrainingStatsData = {
datasets: [{
data: [training, training_injury, training_free],
backgroundColor: ['#36a2eb', '#e33b3b', '#4bc07d']
}],
labels: [
'Training',
'Injury',
'Free'
]
};
........
}
})
}
use parseInt() to change from a string to int then you can add the strings as they are now numbers
var training = parseInt(training_intervall) + parseInt(training_longrun) + parseInt(training_speedwork + parseInt(training_stabilisation) + parseInt(training_competition);
if you want the result back to a string simply put after this
training=""+training
Two problems in your code:
training_intervall and the other 4 variables you want to add are arrays, you should iterate them.
The values are strings, using + with strings results in a new concatenated string. To convert easily a string number to a number (example "1" to 1), you can:
const myString = "1"
const myNumber = myString * 1 // myNumber = 1

How to push an entire array in javascript based on conditional content of single array element?

I have created a Google script that pushes data every hour from the Capital Bikeshare API to a Google Sheet, but I have noticed that the way I am currently pulling the data doesn't maintain consistency over time. Here's the code I'm using:
function myFunction() {
// Set the active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var currentData = ss.getSheetByName("Current");
var historicData = ss.getSheetByName("Historic");
// Fetch API
var stationInfo = UrlFetchApp.fetch('https://gbfs.capitalbikeshare.com/gbfs/en/station_information.json');
var stationStatus = UrlFetchApp.fetch('https://gbfs.capitalbikeshare.com/gbfs/en/station_status.json');
// Get the current date and time
var today = new Date();
var date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
var dateTime = date+' '+time;
// Parse the JSON reply
var jsonInfo = stationInfo.getContentText();
var dataInfo = JSON.parse(jsonInfo);
var jsonStatus = stationStatus.getContentText();
var dataStatus = JSON.parse(jsonStatus);
// Create the data frame for every BID station
var stationInfo72 = dataInfo["data"]["stations"][69];
var stationStatus72 = dataStatus["data"]["stations"][69];
var stationInfo87 = dataInfo["data"]["stations"][83];
var stationStatus87 = dataStatus["data"]["stations"][83];
var stationInfo330 = dataInfo["data"]["stations"][311];
var stationStatus330 = dataStatus["data"]["stations"][311];
var stationInfo153 = dataInfo["data"]["stations"][143];
var stationStatus153 = dataStatus["data"]["stations"][143];
var stationInfo226 = dataInfo["data"]["stations"][213];
var stationStatus226 = dataStatus["data"]["stations"][213];
var stationInfo365 = dataInfo["data"]["stations"][342];
var stationStatus365 = dataStatus["data"]["stations"][342];
var stationInfo473 = dataInfo["data"]["stations"][446];
var stationStatus473 = dataStatus["data"]["stations"][446];
var outputStationsInfo = [stationInfo72, stationInfo87, stationInfo330, stationInfo153, stationInfo226, stationInfo365, stationInfo473]
var outputStationsStatus = [stationStatus72, stationStatus87, stationStatus330, stationStatus153, stationStatus226, stationStatus365, stationStatus473]
Logger.log(outputStationsInfo, outputStationsStatus)
// Create lists of each element
var outputHead = [];
var outputTail = [];
outputStationsInfo.forEach(function(elem,i) {
outputHead.push([elem["station_id"],elem["name"],elem["capacity"], elem["lat"], elem["lon"]]);
});
outputStationsStatus.forEach(function(elem,i) {
outputTail.push([elem["num_bikes_available"], elem["num_ebikes_available"], dateTime]);
});
// Publish arrays in the Current sheet
currentData.getRange(2,1,7,5).setValues(outputHead);
currentData.getRange(2,6,7,3).setValues(outputTail);
// Publish arrays in the Historic sheet
historicData.getRange(historicData.getLastRow() + 1,1,7,5).setValues(outputHead);
historicData.getRange(historicData.getLastRow() - 6,6,7,3).setValues(outputTail);
}
Essentially, I am drilling into the 69th item in the indexes of the JSONs to get the data that I need from two different APIs, and then I merge them together to create a data frame of everything I need to push to the sheet. However, sometimes the API does not report them in the normal order and I end up getting bikeshare stations that aren't in my study area. For example, 99% of the time the 69th item in the array is station_id = 72, but occasionally it's station_id = 73 or something.
Is there a way to conditionally pull a specific array based on the station_id number within the array? I feel like the answer might allow me to do a loop as well to clean this up. Any advice is helpful, as I'm super new to this.
You have to check if the element's station_id is as expected. If not, check through the surrounding parts of the array using a custom iterator.
Snippet:
/**
* #return indexes of the surrounding ``i`` in batches of 5
*/
function* checkSurroundings(i, lastIndex) {
let j = i;
function* check(ct, border, reverse = true, limit = border < 5 ? border : 5) {
const margin = reverse ? ct - limit : ct + limit;
while (ct - margin !== 0) yield reverse ? --ct : ++ct;
return ct;
}
while (i !== 0 || j < lastIndex) {
if (i !== 0) i = yield* check(i, i);
if (j < lastIndex) j = yield* check(j, lastIndex - j, false);
//console.log({ i, j });
}
}
var stations = dataInfo["data"]["stations"];
var stationInfo72 = stations[69];
const iter = checkSurroundings(69,stations.length-1)
//if station_id is not 72, loop through the surrounding indexes
while(stationInfo72["station_id"] !== 72){
const next = iter.next();
if(next.done) {
console.error("station id 72 not found");
break;
}
stationInfo72 = stations[next.value]
}
Snippet showing how checkSurroundings iterates:
/**
* #return indexes of the surrounding ``i`` in batches of 5
*/
function* checkSurroundings(i, lastIndex) {
let j = i;
function* check(ct, border, reverse = true, limit = border < 5 ? border : 5) {
const margin = reverse ? ct - limit : ct + limit;
while (ct - margin !== 0) yield reverse ? --ct : ++ct;
return ct;
}
while (i !== 0 || j < lastIndex) {
if (i !== 0) i = yield* check(i, i);
if (j < lastIndex) j = yield* check(j, lastIndex - j, false);
console.log({ i, j });
}
}
console.log("Order of iteration",[...checkSurroundings(50, 100)])
Conditionally picking elements: filter
For conditionally picking elements from an array in JavaScript, Array.prototype.filter should always be a consideration.
Create a predicate function that matches the shape of your data and checks for certain station IDs.
Here is a function that returns a predicate function. You put in the IDs you want in an array, and it returns the required function for filter.
function byStationId(stationIds) {
return function (obj) {
return stationIds.indexOf(obj.station_id) > -1;
};
}
var myStationFilter = byStationId([72, 73, 74]);
var outputStationsInfo = dataInfo.data.stations.filter(myStationFilter);
Transforming data: map
The pattern
var newArray = [];
oldArray.forEach(function (item) {
newArray.push(/* something based on item */);
});
can usually be replaced with Array.prototype.map
var newArray = oldArray.map(function (item) { return /* something based on item */});
Think of this as the "adapter" from one data shape to another.
function cleanInfo(info) {
return [info.station_id, info.name, info.capacity, info.lat, info.lon];
}
var outputHead = outputStationsInfo.map(cleanInfo);
For the dateTime injection, just do the same trick demonstrated above with the station IDs: have a function that takes a date string and returns the appropriate adapter function.
(Also note the provided date formatting utility Apps Scripts provides, Utilities.formatDate())
var dateTime = Utilities.formatDate(
new Date(),
ss.getSpreadsheetTimeZone(),
"yyyy-MM-dd HH:mm:ss"
);
function cleanStatus(dateTime) {
return function (status) {
return [status.num_bikes_available, status.num_bikes_available, dateTime];
};
}
var outputTail = outputStationsStatus.map(cleanStatus(dateTime));
Here's everything together, untested, just for inspiration. You must at the very least update the line with the station IDs to match your desired station IDs. Note that the helper functions for map and filter are at the bottom, taking advantage of JavaScript's hoisting feature.
function myFunction() {
// Set the active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the current date and time
var dateTime = Utilities.formatDate(
new Date(),
ss.getSpreadsheetTimeZone(),
"yyyy-MM-dd HH:mm:ss"
);
// Fetch API
var stationInfo = UrlFetchApp.fetch(
"https://gbfs.capitalbikeshare.com/gbfs/en/station_information.json"
);
var stationStatus = UrlFetchApp.fetch(
"https://gbfs.capitalbikeshare.com/gbfs/en/station_status.json"
);
// Parse the JSON reply
var dataInfo = JSON.parse(stationInfo.getContentText());
var dataStatus = JSON.parse(stationStatus.getContentText());
// Create the data frame for every BID station
var myStationFilter = byStationId([72, 73, 74]); //!! UPDATE THESE NUMBERS
var outputStationsInfo = dataInfo.data.stations.filter(myStationFilter);
var outputStationsStatus = dataStatus.data.station.filter(myStationFilter);
// Create lists of each element
var outputHead = outputStationsInfo.map(cleanInfo);
var outputTail = outputStationsStatus.map(cleanStatus(dateTime));
// Publish arrays in the Current sheet
var currentData = ss.getSheetByName("Current");
currentData.getRange(2, 1, 7, 5).setValues(outputHead);
currentData.getRange(2, 6, 7, 3).setValues(outputTail);
// Publish arrays in the Historic sheet
var historicData = ss.getSheetByName("Historic");
historicData
.getRange(historicData.getLastRow() + 1, 1, 7, 5)
.setValues(outputHead);
historicData
.getRange(historicData.getLastRow() - 6, 6, 7, 3)
.setValues(outputTail);
//-------- helper functions ------------
function byStationId(stationIds) {
return function (obj) {
return stationIds.indexOf(obj.station_id) > -1;
};
}
function cleanInfo(info) {
return [info.station_id, info.name, info.capacity, info.lat, info.lon];
}
function cleanStatus(dateTime) {
return function (status) {
return [status.num_bikes_available, status.num_bikes_available, dateTime];
};
}
}

Creating Sorted Rows with JavaScript

At my wits end, I think I may be going code blind but can't for the life of me figure out what is causing the issue.
The desired outcome is for there to be only one date per row in the data array and as many flights for each date.
This works for the first item, but not any of the others, ending up with duplicate dates.
Where am I going wrong?
Desired data:
example in coming data 5 objects, only two dates.
["2020-02-20", "LGW"]
["2020-02-20", "LTN"]
["2020-02-20", "LHR"]
["2020-02-26", "LTN"]
["2020-02-26", "LHR"]
an array of two objects (one for each date), with the flights an array by date in the respective date object.
data = [ ["2020-02-20", ["LGW","LTN","LHR"]],
["2020-02-26", ["LTN","LHR"]]
]
Code shown below with comments:
function getRows(alternatives) {
var data = [];
for (var i = 0; alternatives.length > i; i++) {
var tmp = new Date(parseInt(alternatives[i].substring(0, 10)) * 1000);
var month = (tmp.getMonth() + 1);
var date = tmp.getFullYear() + "-" + (month < 10 ? "0" + month : month) + "-" + tmp.getDate();
var airport = alternatives[i].slice(11, 14);
var rowData = {
date: date,
flights: []
};
// if data has objects, check to see if the date is in any of the objects, if it isn't then add rowData to data
if (data.length > 0) {
for (var j = 0; data.length > j; j++) {
if (data[j].date === rowData.date) {
//if there are no flights, add the airport, if there are, is the airport already there, if not, add it
if (data[j].flights.length > 0 || !data[j].flights.includes(airport)) {
data[j].flights.push(airport);
}
}
else {
data.push(rowData);
continue;
}
}
}
else {
rowData.flights.push(airport);
data.push(rowData);
}
}
// not working, dupe dates are appearing in the rows
return data;
}
You can use reduce, create the object which has date as key with value consisting of array with date and array of flights. Check if key already exist, if present then only push in the flights array.
const input = [["2020-02-20", "LGW"],
["2020-02-20", "LTN"],
["2020-02-20", "LHR"],
["2020-02-26", "LTN"],
["2020-02-26", "LHR"]];
const output = Object.values(input.reduce((accu, [date, flight]) => {
if(!accu[date]) {
accu[date] = [date, [flight]];
} else {
accu[date][1].push(flight);
}
return accu;
}, {}));
console.log(output);

Google Script does not trigger as expected given row conditions

I have the 2 columns in my table schema:
Column D= Date, i.e. 20180611 [yyymmdd]
Column F= Continuous Value, i.e. 0.1, 0.6, -0.3 etc.
This is what I want to happen:
Check in column D for yesterday's date. Then, take in the corresponding row, and check if column F is greater than 0.5 (for yesterday's date). If TRUE, then send an email alert.
This is the script I have but it does not trigger for some reason. What is wrong with it?
function readCell() {
var sheet = SpreadsheetApp.getActive().getSheetByName('test');
var dates = sheet.getRange('D1:D').getValues();
var date = null;
var dateRow = 0;
var dateCount = dates.length;
var yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
var yesterdayString = yesterday.toDateString();
for (dateRow; dateRow < dateCount; ++dateCount) {
try {
date = dates[dateRow].toDateString();
if (date === yesterdayString) {
++dateRow;
// To account for zero-based array
break;
}
} catch (error) {
Logger.log(error);
}
}
var value = sheet.getRange('F' + dateRow).getValue();
if (value >= 0.5) {
var result = ('Alert found on: ' + date);
MailApp.sendEmail('blabla#gmail.com', 'Alert', result);
}
};
Here is the data
The problem could be due to the use of an open reference D2:D to get values and then use dates.length to set the number of iterations on the for loop because it could be a number too large.
One "quick and dirty" way that could solve the above issue is to replace
var dateCount = dates.length;
by
var dateCount = sheet.getDataRange().getValues().length;

Get the next highest date value after excluding values from an array

I have a myDate variable with the value 18-Nov-2013.Each day its value is being changed.Tommorow this myDate variable will have the value 19-Nov-2013.I have a list of values that i have mapped into a single array named exclude which contains some dates that are to be excluded ,now it has values ["20-Nov-2013",21-Nov-2013", "23-Nov-2010"] .How could i filter my value from the list of values from the exclude array.I need the next highest value from the array.So here i need the value 22-Nov-2013 after tommorrows date.Could someone help me with this.
var excluded = ["30-Nov-2013","01-Dec-2013","02-Dec-2013"];
var myDate = "29-Nov-2013";
var month = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var current = new Date(myDate);
while(true){
current = new Date((current.getDate()+1<10? "0"+(current.getDate()+1):(current.getDate()+1))+ "-" + month[current.getMonth()] + "-" + current.getFullYear());
var checkDate = (current.getDate()<10? "0"+(current.getDate()):(current.getDate()))+ "-" + month[current.getMonth()] + "-" + current.getFullYear();//this is necessary for when the +1 on day of month passes the month barrier
if(-1 == excluded.indexOf(checkDate))
break;
}
alert(checkDate);
I don't know if this is the best approach, or if is the best algorithm, but you may try this:
var myDate = ["17-Nov-2013", "18-Nov-2013"];
var excluded = ["20-Nov-2013", "21-Nov-2013", "23-Nov-2013"];
var months = {"Nov": 10}; // Add others months "Jan": 1, "Fev": 2 etc...
function findExcluded(date)
{
for (var i = 0; i < excluded.length; i++)
{
if (excluded[i] === date)
{
return true;
}
}
return false;
}
function nextDate()
{
var last = myDate[(myDate.length - 1)];
var s = last.split("-");
var d = new Date(s[2], months[s[1]], s[0]);
var next = new Date(d);
var chkDate = "";
do
{
next.setDate(next.getDate() + 1);
chkDate = next.getDate() + "-" + findMonth(next.getMonth()) + "-" + next.getFullYear();
} while(findExcluded(chkDate));
return chkDate;
}
function findMonth(m)
{
var i = 10; // When you fill all months on 'months' array, this variable should start at '0' in order to loop to works.
for (var month in months)
{
if (i == m)
{
return month;
}
i++;
}
}
var nd = nextDate();
alert(nd);
See it woring here.
No code ? Well here will be my method:
1.Get next date for mydate. Say that is var nextDate.
2.Check whether that date exist in the array.
3.If exists add one more day to nextDate. Again check in the array.
4.Do it until you get a date which is not present in your exclude array
For checking whether it exists in the array you can use arrValues.indexOf(nextDateInProperFormat) > -1

Categories

Resources