0

I'm trying to think of the best way to create a function that avoids using loops/while. Any ideas?

Given a function prototype:

{[sd;n:hols]
/ return list of n number of dates <= SD, excluding weekends and hols 

}

Thanks and happy holidays

Jonathon McMurray
  • 2,881
  • 1
  • 10
  • 22
kenwjj
  • 341
  • 1
  • 7
  • 21

3 Answers3

3

You can use lists and the fact that dates in kdb+ are built on underlying integers:

{[sd;n;hols] d where not[d in hols]&1<mod[d:sd-til n]7}

This uses a til to generate the list of dates up to today, then filters using mod and at the same time checks to make sure the remaining dates aren't in the holiday filter list, before using true results to index back into the generated date list. These will be in descending order, but you can use

{[sd;n;hols] reverse d where not[d in hols]&1<mod[d:sd-til n]7}

To have an ascending date order.

Ryan McCarron
  • 889
  • 4
  • 10
  • Thanks for the reply Ryan. Yes this will give all non-holidays and weekends but it will not yield exactly n amount for dates. For example, if n:90, we would like to have a list of 90 dates. – kenwjj Dec 20 '17 at 15:46
  • One way to do that would be generate a longer list and take a sublist of it the correct length. For example, if you generate a list twice as long as necessary before removing weekends & holidays, you can then take the correct number from that list. Modifying Ryan's function to, for example, `{[sd;n;hols] n#d where not[d in hols]&1 – Jonathon McMurray Dec 20 '17 at 15:50
  • It might also be more efficient to restrict the size of the initially generated list (it's unlikely you'll be generating enough dates for this to be important, but it's worth mentioning) `{[sd;n;hols] reverse n#d where not[d in hols]&1 – Ryan McCarron Dec 20 '17 at 15:55
  • Thank you both for your responses. This checks out assuming that the distinct dates in the holiday are not exceedingly large (a man can dream), else the `n#d` will repeat dates as there are not enough dates in the original list. – kenwjj Dec 20 '17 at 16:38
  • @kenwjj if you need to account for that possibility you can use `n sublist` instead of `n#` - but with Ryan's code to calculate the appropriate sized date list, you won't need this as no matter how many holidays you pass in, it should adjust appropriately – Jonathon McMurray Dec 20 '17 at 17:32
  • See [_Q for Mortals_ 2.5 Temporal Data](http://code.kx.com/q4m3/2_Basic_Data_Types_Atoms/#25-temporal-data) – SJT Dec 31 '17 at 07:49
1

An alternative solution would exclude the holidays before calculating the modulus:

{[sd;n;hols] d where 1<mod[d:except[;hols]sd-til n]7}

The components here are similar to Ryan's answer, other than using "except" to exclude the holidays.

In order to extract exactly n days, you can initially generate a larger list and return a sublist of the correct length, e.g.

{[sd;n;hols] n#d where 1<mod[d:except[;hols]sd-til 2*n]7}

Jonathon McMurray
  • 2,881
  • 1
  • 10
  • 22
0

knowing 2000.01.01 and 2000.01.02 are Saturday and Sunday and that mod those dates are 0 and 1 then excluding all dates who's modular is 0 1 I used:

getBusinessDays:{[Dates;N;Hols] N#(Dates*(Dates mod 7) in 2 3 4 5 6) except 2000.01.01,Hols}

Will return the first N business days you entered excluding holidays you chose.

CWD
  • 323
  • 1
  • 6