Algorithm to find meeting time slots where all participants are available - schedule

Came across this question in an interview blog. Given free-time schedule in the form (a - b) i.e., from 'a' to 'b' of n people, print all time intervals where all n participants are available. It's like a calendar application suggesting possible meeting timinings.
Example:
Person1: (4 - 16), (18 - 25)
Person2: (2 - 14), (17 - 24)
Person3: (6 - 8), (12 - 20)
Person4: (10 - 22)
Time interval when all are available: (12 - 14), (18 - 20).
Please share any known optimal algorithm to solve this problem.
I am thinking of the following solution.
Create a currentList of intervals that contain one interval from each person. Initially currentList = [4-16, 2-14, 6-8, 10-22].
Look for the max_start and min_end in currentList and output (max_start, min_end) if max_start < min_end; Update all intervals in currentList to have start value as min_end. Remove the interval that has min_end from currentList and add the next entry in that person's list to currentList.
If max_start >= min_end in previous step, update all intervals in currentList to have start value as max_start. If for any interval i, end > start, replace that interval in currentList with the next interval of the corresponding person.
Based on the above idea, it will run as below for the given example:
currentList = [4-16, 2-14, 6-8, 10-22] max_start=10 >= min_end=8
update start values to be 10 and replace 6-8 with next entry 12-20.
currentList = [10-16, 10-14, 12-20, 10-22] max_start=12 <= min_end=14
add max_start-min_end to output and update start values to 14. Output=[12-14]
currentList = [14-16, 17-24, 14-20, 14-22] max_start=17 >= min_end=16
update start values to be 17 and replace 14-16 with 18-25
currentList = [18-25, 17-24, 17-20, 17-22] max_start=18 <= min_end=20
add max_start-min_end to output and update start values to 20. Output=[12-14, 18-20]
currentList = [20-25, 2-24, - , 2-22]
Terminate now since there are no more entry from person 3.
I have not implemented the above though. I am thinking of a min-heap and max-heap to get the min and max at any point. But I am concerned about updating the start values because updating the heap may become expensive.

Got this today during an interview. Came up with an O(N*logN) solution. Wondering whether there is an O(N) solution available...
Overview: Join individual schedules into one list intervals --> Sort it by intervals' starting time --> Merge adjacent intervals if crossing --> Returning the availability is easy now.
import unittest
# O(N * logN) + O(2 * N) time
# O(3 * N) space
def find_available_times(schedules):
ret = []
intervals = [list(x) for personal in schedules for x in personal]
intervals.sort(key=lambda x: x[0], reverse=True) # O(N * logN)
tmp = []
while intervals:
pair = intervals.pop()
if tmp and tmp[-1][1] >= pair[0]:
tmp[-1][1] = max(pair[1], tmp[-1][1])
else:
tmp.append(pair)
for i in range(len(tmp) - 1):
ret.append([tmp[i][1], tmp[i + 1][0]])
return ret
class CalendarTests(unittest.TestCase):
def test_find_available_times(self):
p1_meetings = [
( 845, 900),
(1230, 1300),
(1300, 1500),
]
p2_meetings = [
( 0, 844),
( 845, 1200),
(1515, 1546),
(1600, 2400),
]
p3_meetings = [
( 845, 915),
(1235, 1245),
(1515, 1545),
]
schedules = [p1_meetings, p2_meetings, p3_meetings]
availability = [[844, 845], [1200, 1230], [1500, 1515], [1546, 1600]]
self.assertEqual(
find_available_times(schedules),
availability
)
def main():
unittest.main()
if __name__ == '__main__':
main()

A starting point, still to optimize a bit, might be the following (code is in Python).
You have the following data (the allPeople list will be clearly created dynamically):
person_1 = ["4-16","18-24"]
person_2 = ["2-14","17-24"]
person_3 = ["6-8","12-20"]
person_4 = ["10-22"]
allPeople = [person_1, person_2, person_3, person_4]
What you might do is to create a list containing all the time slots of the day (i.e. ["0-1", "1-2", "2-3", etc.] as follows:
allTimeSlots = []
for j in range(0,24):
allTimeSlots.append(str(j) + "-" + str(j+1))
and then create a list called commonFreeSlots, which is made of all the time slots that are inside each person's free time slot collection:
commonFreeSlots = []
for j in range(0,len(allTimeSlots)):
timeSlotOk = True
for k in range(0,len(allPeople)):
person_free_slots = parseSlot(allPeople[k])
if allTimeSlots[j] not in person_free_slots:
timeSlotOk = False
break
if timeSlotOk:
commonFreeSlots.append(allTimeSlots[j])
Please note that the function parseSlot is just taking a list of strings (like "2-14","15-16") and returning a list of hourly time slots (like ["2-3","3-4","4-5" etc.] in order to make it comparable with the hourly time slot list allTimeSlots created above:
def parseSlot(list_of_slots):
result = []
for j in range(0,len(list_of_slots)):
start_time = int(list_of_slots[j].split("-")[0])
end_time = int(list_of_slots[j].split("-")[1])
k = 0
while (start_time + k) < end_time:
result.append(str(start_time+k) + "-" + str(start_time+k+1))
k += 1
return result
If I run the above script, I get the following result:
['12-13', '13-14', '18-19', '19-20']
Of course you will have to still work a bit the output in order to aggregate the hours (and having ['12-14','18-20'] instead of the hourly version), but this should be easier I think.
The above solution should work always, however I'm not sure it's optimal, it probably exists a better one. But since you didn't share any attempt yet, I guess you'd just like some tips to get started so I hope this one helps a bit.

I prefer to take a slightly different approach that's set based! I'll let the language elements do the heavy lift for me. As you guys have already figured out I'm making some assumptions that all meetings are on the top of the hour with an interval length of 1 hour.
def get_timeslots(i, j):
timeslots = set()
for x in range (i, j):
timeslots.add((x, x + 1))
return timeslots
if __name__ == "__main__":
allTimeSlots = get_timeslots(0, 24)
person1TimeSlots = get_timeslots(4, 16).union(get_timeslots(18, 24))
person2TimeSlots = get_timeslots(2, 14).union(get_timeslots(17, 24))
person3TimeSlots = get_timeslots(6,8).union(get_timeslots(12, 20))
person4TimeSlots = get_timeslots(10, 22)
print(allTimeSlots
.intersection(person1TimeSlots)
.intersection(person2TimeSlots)
.intersection(person3TimeSlots)
.intersection(person4TimeSlots))

Dmitry's solution is good enough for most scenarios, but here's another take with O(k * N) time and O(k) extra space, where N is the number of meetings, and k is the granularity of your time slots. If every meeting is known to be hourly, then k can be 24. If the meetings are held every 30 minutes, then k can be 48. k can go all the way up to 60 * 24 (your granularity is every minute in a day). You can also let k be the GCD of all times in minutes.
Overview: Make an k-sized array of booleans called A, where each index corresponds to availability in your time granularity. It begins with every slot available. Run over all meetings. Set the indexes between the start and end time of the meeting in A to False. In the end, A holds the free time slots common for everyone. The times must be in minutes for the algorithm to work.
minutesInDay = 60 * 24
def minuteToString(time):
hour = str(int(time / 60))
minute = str(int(time % 60))
if len(hour) == 1:
hour = '0' + hour
if len(minute) == 1:
minute = '0' + minute
return hour + ':' + minute
def stringToMinute(time):
hour, minute = time.split(':')
return 60 * int(hour) + int(minute)
def availableTimeSlots(meetings, k):
freeTime = [True] * k
step = int(minutesInDay / k)
for meet in meetings:
for i in range(int(meet[0] / step), int(meet[1] / step)):
freeTime[i] = False
result = list()
openInterval = False
beg, end = 0, 0
for i, slot in enumerate(freeTime):
if not openInterval and slot:
openInterval = True
beg = i
elif openInterval and not slot:
openInterval = False
end = i
beg = minuteToString(beg * step)
end = minuteToString(end * step)
result.append((beg, end))
return result
def main():
p1 = [
('9:00', '10:30'),
('12:00', '13:00'),
('16:00', '18:00'),
]
p2 = [
('10:00', '11:30'),
('12:30', '14:30'),
('14:30', '15:00'),
('16:00', '17:00'),
]
p3 = [
('00:00', '8:00'),
('12:00', '14:00'),
('18:00', '24:00'),
]
meetings = [
list(map(stringToMinute, meeting)) for p in [p1, p2, p3]
for meeting in p
]
print(meetings)
print(availableTimeSlots(meetings, 48))
if __name__ == '__main__':
main()

The below is the javascript solution for the problem. Its complexity is O(n^3) but since the time is finite it can be considered n^2
function getFreeMeetingTime(allPeople) {
// get a range of time in a day.
// you can pass the min and max from the input if its not 0 to 24 hrs
const allTimeSlotsInDay = getAllTimeSlots();
let tempResult = [];
for (const person of allPeople) {
for (const time of person) {
for (
let i = Number(time.split('-')[0]);
i < Number(time.split('-')[1]);
i++
) {
const val = `${i}-${i + 1}`;
if (!tempResult.includes(val)) {
tempResult.push(val);
}
}
}
}
// merge the times in between. ex '4-5', '5-6' to '4-6'
return mergeTime(
allTimeSlotsInDay.filter((time) => !tempResult.includes(time))
);
}
function mergeTime(timeArray) {
const result = [];
let i = 0;
while (i < timeArray.length) {
const arr = timeArray[i].split('-');
let start = Number(arr[0]);
let end = Number(arr[1]);
let counter = 0;
for (let j = i + 1; j < timeArray.length; j++) {
const jarr = timeArray[j].split('-');
const jstart = Number(jarr[0]);
const jend = Number(jarr[1]);
if (end == jstart || end >= jstart) {
end = jend;
counter++;
}
}
i = counter === 0 ? ++i : i + counter + 1;
result.push(`${start}-${end}`);
}
return result;
}
function getAllTimeSlots() {
const result = [];
for (let i = 0; i < 24; i = i + 1) {
result.push(`${i}-${i + 1}`);
}
return result;
}
/**
* Creating a sample data to test
*/
//sample time slots of persons where they are busy
const person_1 = ['5-6', '18-24'];
const person_2 = ['2-4', '17-24'];
const person_3 = ['6-8', '12-20'];
const person_4 = ['10-22'];
// Getting an array of time schedules where people are busy.
const allPeople = [person_1, person_2, person_3, person_4];
// get data back to result
const result = getFreeMeetingTime(allPeople);
console.log(result)
you can check the detailed code in the below link
https://www.hectane.com/blog/meeting-when-all-people-free

Possibly naive solutions:
1.
For each participant create an integer representation of their schedule where each bit represents if they are free in that half hour / hour slot. Then do a Bitwise and of all strings.
2.
Alternatively, create N tables for N participants where Col1 = TimeSlot and Col2 = Free/Not and join them all.

Calendar scheduling for 3 people (5 days of the week, 90 min slots using system Verilog multi dimensional associative array technique. This uses foreach loop with 2 variables as indexes to fetch the array values)
module calendar_scheduling;
initial begin
string person1[string][string] = {
"Monday": '{
"0000":"No",
"0130":"No",
"0300":"Yes",
"0430":"No",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"Yes",
"1800":"Yes",
"1930":"No",
"21:00":"Yes",
"2230":"Yes"
},
"Tuesday": '{
"0000":"Yes",
"0130":"No",
"0300":"Yes",
"0430":"No",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"Yes",
"1800":"Yes",
"1930":"No",
"21:00":"Yes",
"2230":"Yes"
},
"Wednesday": '{
"0000":"Yes",
"0130":"No",
"0300":"Yes",
"0430":"No",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"Yes",
"1800":"Yes",
"1930":"No",
"21:00":"Yes",
"2230":"Yes"
},
"Thursday": '{
"0000":"Yes",
"0130":"No",
"0300":"Yes",
"0430":"No",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"Yes",
"1800":"Yes",
"1930":"No",
"21:00":"Yes",
"2230":"Yes"
},
"Friday": '{
"0000":"Yes",
"0130":"No",
"0300":"Yes",
"0430":"No",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"Yes",
"1800":"Yes",
"1930":"No",
"21:00":"Yes",
"2230":"Yes"
}
};
string person2[string][string] = {
"Monday": '{
"0000":"No",
"0130":"Yes",
"0300":"No",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"No",
"1200":"No",
"1330":"No",
"1500":"No",
"1630":"Yes",
"1800":"No",
"1930":"Yes",
"21:00":"No",
"2230":"Yes"
},
"Tuesday": '{
"0000":"Yes",
"0130":"Yes",
"0300":"No",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"No",
"1200":"No",
"1330":"No",
"1500":"No",
"1630":"Yes",
"1800":"No",
"1930":"Yes",
"21:00":"No",
"2230":"Yes"
},
"Wednesday": '{
"0000":"Yes",
"0130":"Yes",
"0300":"No",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"No",
"1200":"No",
"1330":"No",
"1500":"No",
"1630":"Yes",
"1800":"No",
"1930":"Yes",
"21:00":"No",
"2230":"Yes"
},
"Thursday": '{
"0000":"Yes",
"0130":"Yes",
"0300":"No",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"No",
"1200":"No",
"1330":"No",
"1500":"No",
"1630":"Yes",
"1800":"No",
"1930":"Yes",
"21:00":"No",
"2230":"Yes"
},
"Friday": '{
"0000":"Yes",
"0130":"Yes",
"0300":"No",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"No",
"1030":"No",
"1200":"No",
"1330":"No",
"1500":"No",
"1630":"Yes",
"1800":"No",
"1930":"Yes",
"21:00":"No",
"2230":"Yes"
}
};
string person3[string][string] = {
"Monday": '{
"0000":"No",
"0130":"Yes",
"0300":"Yes",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"Yes",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"No",
"1800":"No",
"1930":"No",
"21:00":"No",
"2230":"Yes"
},
"Tuesday": '{
"0000":"Yes",
"0130":"Yes",
"0300":"Yes",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"Yes",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"No",
"1800":"No",
"1930":"No",
"21:00":"No",
"2230":"Yes"
},
"Wednesday": '{
"0000":"Yes",
"0130":"Yes",
"0300":"Yes",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"Yes",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"No",
"1800":"No",
"1930":"No",
"21:00":"No",
"2230":"Yes"
},
"Thursday": '{
"0000":"Yes",
"0130":"Yes",
"0300":"Yes",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"Yes",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"No",
"1800":"No",
"1930":"No",
"21:00":"No",
"2230":"Yes"
},
"Friday": '{
"0000":"Yes",
"0130":"Yes",
"0300":"Yes",
"0430":"Yes",
"0600":"Yes",
"0730":"Yes",
"0900":"Yes",
"1030":"Yes",
"1200":"No",
"1330":"Yes",
"1500":"No",
"1630":"No",
"1800":"No",
"1930":"No",
"21:00":"No",
"2230":"Yes"
}
};
foreach(person1[i,j]) begin
//$display("The calendar values are as follows Time : %0s : %0s : %0s : %0s : %0s",i,j,person1[i][j],person2[i][j],person3[i][j]);
if ((person1[i][j]==person2[i][j])&&(person2[i][j]==person3[i][j]))
$display("All the three people are available at time %0s IST on %0s", j, i);
end
end
endmodule

function getFreeMeetingTime(allPeople) {
// get a range of time in a day.
// you can pass the min and max from the input if its not 0 to 24 hrs
const allTimeSlotsInDay = getAllTimeSlots();
let tempResult = [];
for (const person of allPeople) {
for (const time of person) {
for (
let i = Number(time.split('-')[0]);
i < Number(time.split('-')[1]);
i++
) {
const val = `${i}-${i + 1}`;
if (!tempResult.includes(val)) {
tempResult.push(val);
}
}
}
}
// merge the times in between. ex '4-5', '5-6' to '4-6'
return mergeTime(
allTimeSlotsInDay.filter((time) => !tempResult.includes(time))
);
}
function mergeTime(timeArray) {
const result = [];
let i = 0;
while (i < timeArray.length) {
const arr = timeArray[i].split('-');
let start = Number(arr[0]);
let end = Number(arr[1]);
let counter = 0;
for (let j = i + 1; j < timeArray.length; j++) {
const jarr = timeArray[j].split('-');
const jstart = Number(jarr[0]);
const jend = Number(jarr[1]);
if (end == jstart || end >= jstart) {
end = jend;
counter++;
}
}
i = counter === 0 ? ++i : i + counter + 1;
result.push(`${start}-${end}`);
}
return result;
}
function getAllTimeSlots() {
const result = [];
for (let i = 0; i < 24; i = i + 1) {
result.push(`${i}-${i + 1}`);
}
return result;
}
/**
* Creating a sample data to test
*/
//sample time slots of persons where they are busy
const person_1 = ['5-6', '18-24'];
const person_2 = ['2-4', '17-24'];
const person_3 = ['6-8', '12-20'];
const person_4 = ['10-22'];
// Getting an array of time schedules where people are busy.
const allPeople = [person_1, person_2, person_3, person_4];
// get data back to result
const result = getFreeMeetingTime(allPeople);
console.log(result)
min

Related

PowerShell - next day

I am trying to look into some past days to see if they are weekend or bank holiday. If they are failling into weekend, I am trying to run the query with the subsequent Monday of if bank holiday, trying to run it with next day.
The code is below.
function Check-BankHoliday
{
param
(
[Parameter(Position=1, Mandatory=$true)]
$DayChk
)
$JAMSHost="ukjam01apwpd"
if(!(test-path "JD:\")){$null=New-PSDrive JD JAMS $JAMSHost}
$CalendarName = "Default"
$DateTypeName = "BankHoliday"
$BHDates = Get-ChildItem JD:\Calendars\$CalendarName\$DateTypeName | Select-Object StartDate
$BHDayChk=$false
ForEach ($Date in $BHDates)
{
$BHDayDate=$Date.StartDate.ToString("dd/MM/yyyy")
if($BHDayDate -contains $ImpDateChk)
{
$BHDayChk=$true
}
}
Pop-Location
Return $BHDayChk
}
$lastquarter="select convert (char,(SELECT DATEADD(dd, -1, DATEADD(qq, DATEDIFF(qq, 0, GETDATE()), 0))), 23) as lastquarter"
$lastquarter_day=Invoke-Sqlcmd -Query $lastquarter -ServerInstance "UKWAR01APWPD\WHERESCAPE" -Database RJISDWH
$lastquarter_day= $lastquarter_day.lastquarter
$lastquarter_day = $lastquarter_day.trim(' ')
$lastquarter_day=(Get-Date $lastquarter_day).DayOfWeek
write-host $lastquarter_day
$BHDayChk_lastquarter=Check-BankHoliday $lastquarter_day
if ($BHDayChk_lastquarter -OR $lastquarter_day -eq "Sunday" -OR $lastquarter_day -eq "Saturday")
{
Write-Output "$lastquarter_day had been set as BankHoliday or weekend JAMS Calendars and Changing this to LastWorkDay"
#$lastquarter_day= ConvertTo-Date "today -1 workday" -server $JAMSHost
$lastworkday= ConvertTo-Date "today -1 workday" -server $JAMSHost
$lastquarter=$lastworkday.AddDays(1)
}
else
{
Write-Output "$lastquarter_day is not a BankHoliday or weekend in JAMS Calendars"
}
$SQLquery = "
declare #lastquarter varchar = '$lastquarter_day_date'
declare #sql nvarchar(1000)
set #sql = 'select bp1.sedol, b.benchmark_name, b.description,
bp1.price as '' ' +convert(char,#lastquarter,23) + ' ''
FROM
dbo.ds_benchmark_prices bp1
left join [RJISDWH].dbo.ds_benchmark b on b.sedol=bp1.sedol
where convert(char,bp1.price_date,23) = ''$lastquarter_day'' -- last quarter end
order by b.sedol'
exec (#sql)
"
$lastquarter is returning 2022-12-31 which is a weekend ( Saturday) and if condition below is capturing that being Saturday successfully too.
if ($BHDayChk_lastquarter -OR $lastquarter_day -eq "Sunday" -OR $lastquarter_day -eq "Saturday")
Howevever, how would I add day to Saturday so it runs on the following Monday . I have tried
$lastworkday= ConvertTo-Date "today -1 workday" -server $JAMSHost
$lastquarter=$lastworkday.AddDays(1)
write-host $lastquarter
and that would return 20/01/2023 00:00:00
Below would not work
$lastquarter=$lastquarter.AddDays(1) or $lastquarter=$lastquarter_day .AddDays(1)
I get following error
Method invocation failed because [System.String] does not contain a method named 'AddDays'.
At line:47 char:3
$lastquarter=$lastquarter.AddDays(1)
+ CategoryInfo : InvalidOperation: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MethodNotFound

Retrieving product count for each month in the current year laravel

I'm trying to count the number of orders for each month of the current year as follows
//month arrays to loop through
$month = ['1','2','3','4','5','6', '7', '8', '9', '10', '11','12'];
//current year
$year = Carbon::now()->year;
//variable to store each order coubnt as array.
$new_orders_count = [];
//Looping through the month array to get count for each month in the provided year
foreach ($month as $key => $value) {
$new_orders_count[] = Order::whereYear('updated_at', $year)
->whereMonth('updated_at',$value)
->where(['orders.status_id'=>11])
->orWhere(['orders.status_id'=>14])
->orWhere(['orders.status_id'=>4])->count();
}
This gives me the following result
Note that this is returning 48 even where it should return zero or a different value from 48, for example, months 6 through to 12 have no data thus I expect the query to return zero but it is not. Why is this and how can I achieve what I need? I'm using laravel's default timestamp format for the created_at and updated_at fields
//current year
$year = Carbon::now()->year;
//variable to store each order count as array.
$new_orders_count = [];
//Looping through the month array to get count for each month in the provided year
for($i = 1; $i <= 12; $i++){
$new_orders_count[] = Order::whereYear('updated_at', $year)
->whereMonth('updated_at', $i)
->where(function ($query) {
$query->whereIn('orders.status_id', [11, 14, 4]);
})->count();
}
Try like this way. change your query
Order::whereYear('updated_at', $year)
->whereMonth('updated_at', $value)
->where(function ($query) {
$query->whereIn('orders.status_id', [11, 14, 4]);
})->count();
may this help ...

Year pick from vuejs date picker vue-date-pick

Hi i want to pick year in vue-date-pick
<date-pick
v-model="award.date"
:allowed-dates="allowedYears"
type="year"
>
</date-pick>
allowedYears: date => {
return parseInt(date, 10) % 2 === 0;
},
https://dbrekalo.github.io/vue-date-pick/

Counting total of records by year

I want to count the amount of records by year, for each year. These records contain a datecreated field. But the count should include the previous years as well. So counting the years of 2013 should, include those lower years as well, but not yet of 2014 and higher.
Explanation preferably in linq.
(sql is totally fine though)
I tried doing this by grouping by year, and then count for each year. Now only the previous years should be added, for each year.
I know this can be done with a lot where statements and selecting the results, but there should be a better way.
In SQL you need SUM OVER but it is not supported by Linq. You can download yearly data and calculate the cumulative sums in memory.
var fromYear = 2010;
var toYear = 2019;
var yearlyData = Receipts.Where(x => x.DateCreated.Year >= fromYear & x.DateCreated.Year <= toYear)
.GroupBy(x => x.DateCreated.Year)
.Select(x => new { Year = x.Key, Count = x.Count() })
.ToList();
var result = Enumerable.Range(fromYear, toYear - fromYear)
.Select(year => new
{
Year = year,
CumulativeCount = yearlyData.Where(y => y.Year <= year).Sum(y => y.Count)
});
Also you can use an outer variable:
var fromYear = 2010;
var toYear = 2019;
var yearlyData = Receipts.Where(x => x.DateCreated.Year >= fromYear & x.DateCreated.Year <= toYear)
.GroupBy(x => x.DateCreated.Year)
.Select(x => new { Year = x.Key, Count = x.Count() })
.OrderBy(x => x.Year)
.ToList()
;
var sum = 0;
var result = yearlyData.Select(x => new {x.Year, CumulativeSum = sum += x.Count});
select sum(case when year(datecreated) <= 2013 then 1 else 0 end) as until_2013,
sum(case when year(datecreated) <= 2014 then 1 else 0 end) as until_2014,
sum(case when year(datecreated) <= 2015 then 1 else 0 end) as until_2015
from your_table
Here's your query.
select count(1), year(created_date) from tableA
where year(created_date) < 2014
group by year(created_date)

generating json from bad mysql-formatted dates in asp

I was thrown in a new project, which is apparently more than just oudated. The application saves opening hours in a very weird pattern in the DB and this is driving me crazy for over a week now.
Please have a look at this image:
As you might see, the opening hours are saved in a pattern like:
dayFrom | dayTo | timeFrom | timeTo
=======================================
monday | friday | 07:00 | 17:00
saturday | | 08:00 | 12:00
Just to prevent any misunderstanding:
Open MO - FR from 07:00 to 17:00
Open SA from 08:00 to 12:00
Closed on Sunday
Now, this seems to be kinda off already, but sticking with that, a table could look like this:
dayFrom | dayTo | timeFrom | timeTo
=======================================
monday | tuesday | 07:00 | 14:00
wednesday | | 08:00 | 12:00
thursday | friday | 07:30 | 13:00
saturday | | 08:00 | 12:00
So, now my problem: I need to make a loop (or something like this) to create a valid json-string, containing all these opening hours.
Right now, I have this:
jsonAppendix = "{""openingHours"":["
for i = 1 To cint(hoechsterTag)
jsonAppendix = jsonAppendix & "{""dayOfWeek"":" & i & ", ""from1"":""" & rs("ZeitVon") & """, ""to1"":""" & rs("ZeitBis") & """},"
next
'Remove last comma
jsonAppendix = LEFT(jsonAppendix, (LEN(jsonAppendix)-1))
jsonAppendix = jsonAppendix & "]}"
If I have only a "monday-friday", it works already, but the 2nd (or next entries) aren't taken into account.
The output looks like this, which is apparently kinda correct:
{
"openingHours":[
{
"dayOfWeek":1,
"from1":"07:00",
"to1":"17:00"
},
{
"dayOfWeek":2,
"from1":"07:00",
"to1":"17:00"
},
{
"dayOfWeek":3,
"from1":"07:00",
"to1":"17:00"
},
{
"dayOfWeek":4,
"from1":"07:00",
"to1":"17:00"
},
{
"dayOfWeek":5,
"from1":"07:00",
"to1":"17:00"
}
]
}
But the "Saturday" is not being recognized.
My function looks like this:
SQL = "SELECT * FROM StandortOpen WHERE S_ID = " & iStandortId & " AND OpenArt = '" & sArt & "' ORDER BY Sort,OpenArt DESC"
call openRS(SQL)
'day-mapping
tageV(0) = replace(rs("TagVon"),"Mo", 1)
tageV(1) = replace(rs("TagVon"),"Di", 2)
tageV(2) = replace(rs("TagVon"),"Mi", 3)
tageV(3) = replace(rs("TagVon"),"Do", 4)
tageV(4) = replace(rs("TagVon"),"Fr", 5)
tageV(5) = replace(rs("TagVon"),"Sa", 6)
tageV(6) = 7
tageB(0) = replace(rs("TagBis"),"Mo", 1)
tageB(1) = replace(rs("TagBis"),"Di", 2)
tageB(2) = replace(rs("TagBis"),"Mi", 3)
tageB(3) = replace(rs("TagBis"),"Do", 4)
tageB(4) = replace(rs("TagBis"),"Fr", 5)
tageB(5) = replace(rs("TagBis"),"Sa", 6)
'for example: mo - fr
for each item in tageV
'save smallest weekday
if(isNumeric(item) AND item > "") then
if(cint(item) <= cint(niedrigsterTag)) then
niedrigsterTag = cint(item)
end if
end if
next
for each item in tageB
'save highest weekday
if(isNumeric(item) AND item > "") then
if(cint(item) >= cint(hoechsterTag)) then
hoechsterTag = cint(item)
end if
end if
next
And the openRS()-Function:
sub openRS(str_sql)
'Response.write "SQL: " & str_sql & "<br>"
set rs = CreateObject("ADODB.Recordset")
rs.open str_sql,conn,1,3
end sub
So basically: Mapping numbers to the days, iterating through (or compare them to get a timespan).
I'm using a RecordSet as well. Maybe I need to use arrays or something like this? Any help would be really appreciated.
I can't alter the table nor the design of that, I have to stick with that gargabe
If you want to create the dataset in SQL Server, consider the following
Example
Declare #YourTable table (dayFrom varchar(25),dayTo varchar(25),timeFrom varchar(25),timeTo varchar(25))
Insert Into #YourTable values
('monday' ,'tuesday','07:00','14:00'),
('wednesday','' ,'08:00','12:00'),
('thursday' ,'friday' ,'07:30','13:00'),
('saturday' ,'' ,'08:00','12:00')
;with cteD as (Select * From (Values(1,'Monday'),(2,'Tuesday'),(3,'Wednesday'),(4,'Thursday'),(5,'Friday'),(6,'Saturday'),(7,'Sunday')) DDD(DD,DDD) ),
cteR as (
Select A.*
,R1 = B.DD
,R2 = IsNull(C.DD,B.DD)
From #YourTable A
Left Join cteD B on dayFrom = B.DDD
Left Join cteD C on dayTo = C.DDD
Where 1=1 -- Your WHERE STATEMENT HERE
)
Select daySeq = A.DD
,dayOfWeek = A.DDD
,from1 = IsNull(B.TimeFrom,'Closed')
,from2 = IsNull(B.TimeTo,'Closed')
From cteD A
Left Join cteR B on A.DD between B.R1 and B.R2
Order By 1
Returns
Note: The Closed is Optional. Remove the "LEFT" Join in the final query
Now, if you want to create the JSON String in SQL Server, and you're NOT on 2016, we can tweak the final query and add a UDF.
Select JSON=[dbo].[udf-Str-JSON](0,0,(
Select daySeq = A.DD
,dayOfWeek = A.DDD
,from1 = IsNull(B.TimeFrom,'Closed')
,from2 = IsNull(B.TimeTo,'Closed')
From cteD A
Left Join cteR B on A.DD between B.R1 and B.R2
Order By 1
For XML RAW
))
Returned JSON String
[{
"daySeq": "1",
"dayOfWeek": "Monday",
"from1": "07:00",
"from2": "14:00"
}, {
"daySeq": "2",
"dayOfWeek": "Tuesday",
"from1": "07:00",
"from2": "14:00"
}, {
"daySeq": "3",
"dayOfWeek": "Wednesday",
"from1": "08:00",
"from2": "12:00"
}, {
"daySeq": "4",
"dayOfWeek": "Thursday",
"from1": "07:30",
"from2": "13:00"
}, {
"daySeq": "5",
"dayOfWeek": "Friday",
"from1": "07:30",
"from2": "13:00"
}, {
"daySeq": "6",
"dayOfWeek": "Saturday",
"from1": "08:00",
"from2": "12:00"
}, {
"daySeq": "7",
"dayOfWeek": "Sunday",
"from1": "Closed",
"from2": "Closed"
}]
The UDF if Interested
CREATE FUNCTION [dbo].[udf-Str-JSON] (#IncludeHead int,#ToLowerCase int,#XML xml)
Returns varchar(max)
AS
Begin
Declare #Head varchar(max) = '',#JSON varchar(max) = ''
; with cteEAV as (Select RowNr =Row_Number() over (Order By (Select NULL))
,Entity = xRow.value('#*[1]','varchar(100)')
,Attribute = xAtt.value('local-name(.)','varchar(100)')
,Value = xAtt.value('.','varchar(max)')
From #XML.nodes('/row') As R(xRow)
Cross Apply R.xRow.nodes('./#*') As A(xAtt) )
,cteSum as (Select Records=count(Distinct Entity)
,Head = IIF(#IncludeHead=0,IIF(count(Distinct Entity)<=1,'[getResults]','[[getResults]]'),Concat('{"status":{"successful":"true","timestamp":"',Format(GetUTCDate(),'yyyy-MM-dd hh:mm:ss '),'GMT','","rows":"',count(Distinct Entity),'"},"retults":[[getResults]]}') )
From cteEAV)
,cteBld as (Select *
,NewRow=IIF(Lag(Entity,1) over (Partition By Entity Order By (Select NULL))=Entity,'',',{')
,EndRow=IIF(Lead(Entity,1) over (Partition By Entity Order By (Select NULL))=Entity,',','}')
,JSON=Concat('"',IIF(#ToLowerCase=1,Lower(Attribute),Attribute),'":','"',Value,'"')
From cteEAV )
Select #JSON = #JSON+NewRow+JSON+EndRow,#Head = Head From cteBld, cteSum
Return Replace(#Head,'[getResults]',Stuff(#JSON,1,1,''))
End
-- Parameter 1: #IncludeHead 1/0
-- Parameter 2: #ToLowerCase 1/0 (converts field name to lowercase
-- Parameter 3: (Select * From ... for XML RAW)
-- Syntax : Select [dbo].[udf-Str-JSON](0,1,(Select Top 2 RN=Row_Number() over (Order By (Select NULL)),* from [Chinrus-Shared].[dbo].[ZipCodes] Where StateCode in ('RI') for XML RAW))
/*
Declare #User table (ID int,Active bit,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50))
Insert into #User values
(1,1,'John','Smith','john.smith#email.com'),(2,0,'Jane','Doe' ,'jane.doe#email.com')
Declare #XML xml = (Select * from #User for XML RAW)
Select A.ID
,B.JSON
From #User A
Cross Apply (Select JSON=[dbo].[udf-Str-JSON](0,0,(Select A.* For XML Raw)) ) B
*/