I have been stuck on this problem for a while now, so I think it is time to ask my cool worldwide geniuses.
What I am trying to do: Create An array of arrays that pairs a date and data automatically (through a loop). Complicating factors including trying to get dates of only weekdays and exclude holidays.
Problem: When I use a variable, the API data doesn't display. This particular line is the problem arrayOfDateAndPrice.push(data[2].data['Time Series (Daily)'][today]['4. close'])
the 'data' holds the API that I am fetching. I am digging through the JSON in data[2].data['Time Series'] etc. When I insert the date with a variable (i.e date I got from the loop, I checked, it fetches the correct date in string format). it doesn't work. BUT, when I insert a stringed normal date (i.e. '2022-03-04') it displays the data!? What the heck am I missing?
Error: TypeError: undefined is not an object (evaluating 'data[2].data['Time Series (Daily)'][today]['4. close']')
const calculateBusinessDay = (data) => {
const presDay = '02-21-2022';
const goodFri = '04-15-2022';
const memDay = '05-30-2022';
const junTeenth = '06-20-2022';
const july4th = '07-04-2022';
const laborDay = '09-05-2022';
const thanksGiving = '11-24-2022';
const xMas = '12-26-2022';
var today;
var arrayOfDateAndPrice = [];
const arrayOfAllData = [];
const weekdays = momentBusiness
weekdays.updateLocale('us', {
holidays: [mlk, presDay, goodFri, memDay, junTeenth, july4th, laborDay, thanksGiving, xMas],
holidayFormat: 'MM-DD-YYYY',
workingWeekdays: [1, 2, 3, 4, 5]
});
for (let i = 0; i < 30; i++) {
today = weekdays().subtract(i, 'days').format('YYYY-MM-DD')
if (weekdays(today).isBusinessDay() && !weekdays(today).isHoliday() == true) {
arrayOfDateAndPrice.push(today)
arrayOfDateAndPrice.push(data[2].data['Time Series (Daily)'][today]['4. close'])
arrayOfAllData.push(arrayOfDateAndPrice)
arrayOfDateAndPrice = [];
}
}
return (
arrayOfAllData
)
}
I figured it out?!?! It was because my 'i' variable started the day from "today" which was Japan time. No market data for Japan time since we are "ahead" in time lol. Oh gosh. That feels good...
The 'i' had to start at i=1, to subtract one day from today and start the loop.
Related
I'm facing a problem and I'm stuck and lost so kindly get some help from the community
I have a structure in my Object Array called items where I have an Item (Level). This array have 4 in total.
const items: any[] = await sp.web.lists.getByTitle("List")
.items.select("ID, Part, Level, MyDate")
.filter("Part eq 1")
.getAll();
My problem is manipulating the Array in order to get:
Get the duplicates for Level, in this case is (2) remove all others;
From that 2 remove the one with the oldest Date.
So I'll have only one.
Couldn't get help from my research.
This is the solution for my problem, sometimes workload block us
//Find the duplicates based on Level and remove all others
workItems = items
const lookup: any[] = workItems.reduce((a, e) => {
a[e.Level] = ++a[e.Level] || 0;
return a;
}, {});
const levels = workItems.filter(e => lookup[e.Level]);
//Get the latest date from my duplicates and remove all older
let isLast = levels[0];
for (let date of levels) {
if (moment(date.YourDateField) > moment(isLast.YourDateField)) {
isLast = date;
}
}
I am a beginner in google app script and would like to ask a question regarding how to create subclasses for an object based on an array of information drawn from my spreadsheet.
Here is an example sheet with some data in the sheet "History". The input data is the transactional history of the investment of a user. My end goal is to create an array inside google app script with adjusted stock-split values for any given stock.
However, the first step in my project would be to gather the data in such a manner that I can perform these calculations. For this, I would need to create an object such as this:
stock symbol: {date:value, {quantity: value, price:value}}, {date:value, {split ratio:value}}
The reason for this is because in this object the dates are linked to quantity price and split ratio. In later calculations I would look if the date of the split value is less or equal to the date of the quantity/price value, if this is true then perform split ratio * quantity and price/split ratio. If this is not true, then leave the price and quantity as is, for any given stock. Finally return these object in the same form as the orginal array.
This is the attempt I have made so far:
function createDate(date, quantity, price) {
this.date = date;
this.quantityPrice = new createDateData (quantity, price);
}
function createDateData(quantity, price) {
this.quantity = quantity;
this.price = price;
}
function retrieveData () {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const inputSheet = ss.getSheetByName('History');
const data = inputSheet.getRange(2, 1, inputSheet.getLastRow() - 1, 9).getValues();
const filterd = data.filter(row => row[2] == 'Buy' || row[2] == 'Sell' || row [2] == 'Split');
const sorted = filterd.sort((a, b) => {
if (a[0] < b[0]) return -1
if (a[0] > b[0]) return 1
else return 0
})
for ( let i of sorted) {
var sampleData= new createDate([i][0][0],[i][0][3],[i][0][4]);
console.log(sampleData);
}
}
This is the output I get
{ date: Tue Jun 30 2020 18:00:00 GMT-0400 (Eastern Daylight Time),
quantityPrice: { quantity: 1, price: 40000 } }
Which is different than from the desired output?
Question: How do I get the desired output? For example, in the case of AMZN:
AMZN: {9/28/2020, {1, 100}}, {9/28/2020, {0.5, 200}}, {10/19/2020 {0.2, 100}}, {11/27/2020, {10}}
EDIT2: Please see sheet "Desired Output" for desired output.
As far as I can tell you have the array formula in column 'Price', so you need to update the column 'Quantity' only. The column 'Price' will updated automatically.
And are you sure that the line 27 in your desired output should change? I think it should not, since it's 'Cash', not 'AMZN'.
Try this code:
function main() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('History');
var range = sheet.getRange(3,1,sheet.getLastRow(),9);
var data = range.getValues().reverse(); // reverse to iterate it from the top to the bottom
for (var row in data) {
var action = data[row][2];
if (action != 'Split') continue;
data = [...data.slice(0,row), ...update(data.slice(row))]; // update all rows below the 'Split'
}
// get column 'Quantity' from the data and put it on the sheet
var quantity = data.map(x => [x[3]]).reverse();
sheet.getRange(3,4,data.length,1).setValues(quantity);
}
// the function takes rows, recalculates quantity and returns the updated rows
function update(data) {
var security = data[0][1];
var ratio = data[0][8];
// if the security is the same as in the first row,
// and if the action is 'Buy' or 'Sell':
// the price will be multiply by the ratio
for (var row in data) {
if (data[row][1] != security) continue;
var action = data[row][2];
if (['Buy','Sell'].includes(action)) data[row][3] *= ratio;
try {if (row[+r+1][1] == 'Split') break} catch(e) {} // <-- updated line
}
return data;
}
The script implies that all the rows are sorted by date. Newest dates at the bottom, oldest dates at the top.
There is no any objects. I see no need for them. Probably it could be done another way, with objects.
I am trying to filter the array 'employee_name' consisting of NaNs and one string element, to exclude any element BUT the string. The context is that I have a spreadsheet containing employee's birth dates, and I'm sending an email notification in case there's a birthday two days from today. My variables look like this:
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Employees');
var range = ss.getRange(2, 1, ss.getLastRow()-1, 1); // column containing the birth dates
var birthdates = range.getValues(); // get the `values` of birth date column
var today = new Date ();
var today = new Date(today.getTime());
var secondDate = new Date(today.getTime() + 48 * 60 * 60 * 1000);
var employee_name = new Array(birthdates.length-1);
And the loop:
for (var i=0;i<=birthdates.length-1;i=i+1){
var fDate = new Date(birthdates[i][0]);
if (fDate.getDate() == secondDate.getDate() &&
fDate.getMonth() == secondDate.getMonth()){
//define variables for outgoing email
for (var j=0; j<=birthdates.length-1;j=j+1){
employee_name[j] = [NaN];
}
employee_name[i] = ss.getRange(i+2,6);
employee_name[i] = employee_name[i].getValues();
}
}
after which the array in question looks like this
Logger.log(employee_name);
[[[Mia-Angelica]], [NaN], [NaN], [NaN], ..., [NaN]]
I have already tried the filter(Boolean), but this isn't working:
employee_name_filtered = employee_name.filter(Boolean);
Logger.log(employee_name_filtered);
returns [[[Mia-Angelica]], [NaN], [NaN], [NaN], ..., [NaN]].
I have also tried filling the non-string array entries with numeric values (instead of NaN) and then apply
employee_name_filtered = employee_name.filter(isFinite);
Logger.log(employee_name_filtered);
returns [[1.0], [2.0], [3.0], ..., [72.0]], so this filter method is working, but then I would need the 'inverse' of that because I want to keep the string.
I need the array within array to store the values at the position of the counter variable where the condition's met (similar to How to store data in Array using For loop in Google apps script - pass array by value).
This is my first time posting a question on SO, so if I overlooked any 'rules' about posting, just let me know and I will provide additional info.
Any help will be appreciated!
EDIT:
what I would like to receive in the end is simply
[[Mia-Angelica]].
The array you are using a 2 dimensional array - meaning it's an array of arrays so the filter method you are using cannot be applied in the same manner.
For this, I suggest you try the below snippet.
function cleanArray() {
var initialArray = [
['Mia-Angelica'],
['Space'],
['2'],
[NaN],
[NaN],
[NaN],
[NaN]
];
var finalArray = [];
for (let i = 0; i < initialArray.length; i++) {
var midArray = initialArray[i].filter(item => (Number.isFinite(item) && item.id !== 0) || !Object.is(item, NaN));
finalArray.push(midArray);
}
console.log(finalArray.filter(item => item != ''));
}
Note
Please bear in mind that getValues will return an Object[][] which is a two-dimensional array of values.
Reference
Apps Script Range Class;
Array.prototype.filter().
Hi I want to make a function that generates available time slots. It should generate the time slots while keeping in mind that the time slot can't overlap with an already made appointment.Before the time slots are generated a user can specify which kind of appointment to schedule. Each appointment sort has a duration. So it should also check if the time slot added with the duration doesn't overlap.
I'm struggling to make this all working so far I get time slots but it seems to only checks the start of an already made appointment. I'm kind of running in circles here and would love for some advice or part solutions that I can implement to make my idea work
const GenerateAvailableTimeSlots = (start, serviceObject, allAppointments) => {
const moment = extendMoment(Moment);
var x = {
nextSlot: 15,
appointmentsOfThatDay: [],
startTime: '8:00',
endTime: '20:00'
};
// function to filter only the appointment that occur on specified day --> ( start )
let filterAppointments = (allAppointments, start) => {
let results = [];
let filterAppoinments = allAppointments.filter(appoinment => appoinment.date === start.format('MMMM Do YYYY'));
filterAppoinments.map(appoinment => results.push([appoinment.start.format('HH:mm'), appoinment.end.format('HH:mm')]))
console.log("results", results);
return results;
};
x.appointmentsOfThatDay = filterAppointments(allAppointments, start)
console.log("appointmentsOfThatDay", x.appointmentsOfThatDay)
var slotTime = moment(x.startTime, "HH:mm");
var endTime = moment(x.endTime, "HH:mm");
// function to check time slot overlaps with already made appointments
function OverlapsScheduledAppointment(slotTime, appointments) {
//added duration to timeslot so I could check if a suggested timeslot + the duration also doesn't overlap with already made appointment
var slotTimeWithDuration = slotTime.clone().add(serviceObject.hours, 'hours').add(serviceObject.minutes, 'minutes');
// I don't know where I also could check for slotTimeWithDuration overlap
return appointments.some((br) => {
console.log(slotTime >= moment(br[0], "HH:mm") && slotTime < moment(br[1], "HH:mm"));
return (slotTime >= moment(br[0], "HH:mm") && slotTime < moment(br[1], "HH:mm"));
});
}
let times = [];
while (slotTime < endTime) {
if (!OverlapsScheduledAppointment(slotTime, x.appointmentsOfThatDay)) {
times.push(slotTime.format("HH:mm"));
}
slotTime = slotTime.add(x.nextSlot, 'minutes');
}
return times;
};
I've found the answer to my question.
I was going in the right direction with the above code but in order for generating available time slots that keep in mind the duration of the service you want to schedule and the appointment that are already scheduled.
I had to change this line of code:
// this line just pushes the filtered appointment for a specific day
filterAppoinments.map(appoinment => results.push([appoinment.start.format('HH:mm'), appoinment.end.format('HH:mm')]))
To this
// this line filters the appointment for a specific day and also adds the duration of a service to the start time of an already scheduled appointment. This way when I check if a generated time slot for a service will overlap with an already scheduled appointment it filters out the ones that will overlap
filterAppoinments.map(appoinment => results.push([appoinment.start.clone().subtract(serviceObject.hours, 'hours').subtract(serviceObject.minutes, 'minutes').format('HH:mm'), appoinment.end.format('HH:mm')]))
A new React developer here!
I'm trying to check if my array's dates have certain symbols in them e.g. "2019" or "-11-". I need to see if the array[i] doesn't have a date to add the inputted date to that slot. I could use !== undefined or isNaN, but it gives a number or empty as an error for some reason...
I've tried many different approaches, but I feel like this could be the way to go. Unless someone else figures out a better solution that is :D
Thanks for your time ^_^ Hopefully this is an easy fix that I just didn't notice!
/*
date = user-inputted date (XXXX-XX-XX)
dateData = {name, dates} in database ("name", "XXXX-XX-XX")
newOrder[] = dateData.dates, but rearranged to a correct order, no date
in the spot where "date" should be added
*/
let newOrder = [dateData.length + 1];
for (let i = 0; i < newOrder.length; i++) {
if (newOrder[i] /*solution here*/) {
newOrder[i] = moment(date).format("YYYY-MM-DD");
break;
}
}
// after this I'll update the database with async
When using moment, you could use the moment#isValid method to check if the current value is a valid date.
Although, I don't find it really clear what you are trying to achieve. Perhaps you can update your example with some data.
let newOrder = [dateData.length + 1];
for (let i = 0; i < newOrder.length; i++) {
const parsedDate = moment(date);
if (newOrder[i] && parsedDate.isValid()) {
newOrder[i] = parsedDate.format("YYYY-MM-DD");
break;
}
}
You can try this code.
"3-12-2019".indexOf('2019')
or
"3-12-2019".startsWith('2019') || "3-12-2019".endsWith('2019')
With moment you can get day, month and year.
var check = moment('2014-07-28', 'YYYY/MM/DD');
var month = check.format('M');
var day = check.format('D');
var year = check.format('YYYY');
console.log(month, day, year);
Considering this: dateData.dates is the Array where your dates are.
// Loops through the dates in the Array[]
dateData.dates.forEach(date => {
// Checks whether the date exists
// !date: if date = '' or null or any other falsy values will return true
if(!date){
date = moment(date).format("YYYY-MM-DD");
}
});