计算资源的可用性 [英] Calculate availabilites of a resource

查看:43
本文介绍了计算资源的可用性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序,显示一个星期(资源)的可预订时段.

一个时隙总是30分钟长,可以从:00或:30开始.

内部可用的时间以一周中的分钟数(以数组的形式)内部表示, 0 表示一周中午夜的第一分钟,而 10079 则是一周的最后一分钟.这意味着具有100%可用性的资源在一个数组中具有10080个数字,范围是0到10079.

例如这表示本周第一天的09:55-11:05之间有70分钟的可用时间:

  [595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642、643、644、645、646、648、649、650、651、652、653、654、655、656、657、658、659、660、661、662、663、664、665]] 

我将如何计算和显示任何可能的时隙(即至少连续30分钟)?

给出上面和当前一周的数据集,并将星期一作为一周的第一天:

  [2016年8月8日星期一,2016年8月8日星期一10:30:00][2016年8月8日星期一10:30:00,2016年8月8日星期一11:00:00] 

我不知道这是否容易,但是我目前不知道如何使用javascript做到这一点?任何提示都非常感谢!

解决方案

这是一个基本函数,它需要一个间隔长度 n 和一个数字数组(代表分钟的可用性)./p>

通过调用 getIntervals(30)(availableMinutes),我们将获得所有可用时隙…

请注意,下面的 range 过程仅用于创建示例数据.您无需将其包含在程序中.

 //getIntervals :: Number->[Number]->[{从:号码,到:号码}]const getIntervals = n =>可用性=>{//通过减少可用性计算的间隔...让{slots} = Availability.reduce(({{slots,count,prev},m)=> {//在第一分钟初始化状态如果(上一个===未定义)返回{slots,count,prev:m}//如果当前间隔为空,则必须从间隔标记开始否则if(count === 0&&prev%n!== 0)返回{slots,count,prev:m}//如果当前分钟不是连续的,则重新搜索下一个间隔否则(prev + 1!== m)返回{slots,count:0,prev:m}//如果当前间隔已完成,则连接有效间隔否则(计数=== n-1)返回{slots:[... slots,{from:m-n,to:prev}],count:0,prev:m}//否则,当前分钟是连续的,添加到当前间隔别的返回{slots,count:count + 1,prev:m}},{插槽:[],计数:0,上一个:未定义})//从减少计算中返回`slots'值返回插槽}//范围::数字->编号->[数字]const range = min =>max =>{let loop =(res,n)=>n === max?res:循环([... res,n],n + 1)返回循环([],最小值)}//创建样本数据让可用性= [... range(55)(400),//[55,56,57,...,399]... range(3111)(3333),//[3111,3112,3113,...,3332]... range(8888)(9000)//[8888,8889,8890,...,8999]]//获取间隔console.log(getIntervals(30)(可用性)) 

只需确保这些分钟标记是 UTC时间戳的分钟偏移量,一切就将变得容易.要显示这些分钟标记,只需在设置为资源周开始时间的UTC时间戳中添加 X 分钟即可.

我们将在此处创建一个名为 timestampAddMinutes 的小程序,该程序将分钟数根据资源的时间戳记 week

转换为Date对象.

然后,我们创建一个 getIntervalDates 过程,将其应用于数组中每个间隔的每个 from to

 //timestampAddMinutes ::日期->编号->日期const timestampAddMinutes = t =>m =>{令d = new Date(t.getTime())d.setMinutes(t.getMinutes()+ m)返回d}//getIntervalDates :: [{{from:Minute,to:Minute}]->[{从:日期,到:日期}]const getIntervalDates =间隔=>{return interval.map(({{from,to})=>({来自:timestampAddMinutes(星期)(来自),至:timestampAddMinutes(星期)(至)}))}//为资源采样时间戳//周一午夜,时区偏移量0让周=新日期("2016-08-08 00:00:00 +0000")//上一个代码段的间隔结果让间隔= [{from:60,to:89},{from:90,to:119},{from:120,to:149},{from:150,to:179},{from:180,to:209},{from:210,to:239},{from:240,to:269},{from:270,to:299},{from:300,to:329},{from:330,to:359},{from:360,to:389},{from:3120,to:3149},{from:3150,to:3179},{from:3180,to:3209},{from:3210,to:3239},{from:3240,to:3269},{from:3270,to:3299},{from:3300,to:3329},{from:8910,to:8939},{from:8940,to:8969}]//将间隔转换为日期console.log(getIntervalDates(intervals)) 

您将看到 from to 分别转换为 Date 对象.还请注意我的浏览器(在EDT时区中)如何自动将UTC时间戳转换为在当前时区中显示.上面的输出将以 TZ 字符串格式显示它们,但是它们是完全可用的Date对象.这意味着您可以在其上调用任何 Date方法以获取特定的详细信息,例如星期几,小时或分钟等.

并且由于我们的 getIntervals 函数正常运行,因此您还会看到每个间隔为30分钟,从:00 :30开始

输出

  [{从:2016年8月7日,格林尼治标准时间-0400(EDT),至:2016年8月7日,格林尼治标准时间-0400(EDT)},{从:2016年8月7日,星期日21:30:00 GMT-0400(EDT),至:2016年8月7日,星期日21:59:00 GMT-0400(EDT)},{from:Sun Aug 07 2016 22:00:00 GMT-0400(EDT),至:Sun Aug 07 2016 22:29:00 GMT-0400(EDT)},{从:2016年8月7日,星期日22:30:00 GMT-0400(EDT),至:2016年8月7日,星期日22:59:00 GMT-0400(EDT)},{从:2016年8月7日,格林尼治标准时间0400(EDT),至:2016年8月7日,格林尼治标准时间-0400(EDT)},{从:2016年8月7日,星期日23:30:00 GMT-0400(EDT),到:2016年8月7日,星期日23:59:00 GMT-0400(EDT)},{from:2016年8月8日星期一00:00:00 GMT-0400(EDT),至:2016年8月8日星期一0:29:00 GMT-0400(EDT)},{从:2016年8月8日星期一0:30:00 GMT-0400(EDT),到:1月8日星期一8:59:00 GMT-0400(EDT)},{from:Mon Aug 08 2016 01:00:00 GMT-0400(EDT),to:Mon Aug 08 2016 01:29:00 GMT-0400(EDT)},{从:2016年8月8日星期一10:00:30:00 GMT-0400(EDT),到:1月8日星期一8:59:00 GMT-0400(EDT)},{from:Mon Aug 08 2016 02:00:00 GMT-0400(EDT),to:Mon Aug 08 2016 02:29:00 GMT-0400(EDT)},{从:2016年8月10日星期三00:00:00 GMT-0400(EDT),至:: 2016年8月10日星期三00:29:00 GMT-0400(EDT)},{从:2016年8月10日星期三00:30:00 GMT-0400(EDT),至:: 2016年8月10日星期三00:59:00 GMT-0400(EDT)},{从:2016年8月10日星期三01:00:00 GMT-0400(EDT),到:3月10日2016年8月10日01:29:00 GMT-0400(EDT)},{from:2016年8月10日星期三01:30:00 GMT-0400(EDT),至:2016年8月10日星期三01:59:00 GMT-0400(EDT)},{从:2016年8月10日星期三02:00格林尼治标准时间-0400(EDT),至:: 2016年8月10日星期三02:29:00 GMT-0400(EDT)}{from:2016年8月10日星期三02:30:00 GMT-0400(EDT),至:2016年8月10日星期三02:59:00 GMT-0400(EDT)},{从:2016年8月10日星期三03:00:0 GMT-0400(EDT),至:: 3月10日2016年8月2日格林尼治标准时间0400(EDT)},{从:2016年8月14日,星期日0:30:00 GMT-0400(EDT),至::太阳8月14日,00:59:00 GMT-0400(EDT)},{from:Sun Aug 14 2016 01:00:00 GMT-0400(EDT),to:Sun Aug 14 2016 01:29:00 GMT-0400(EDT)}] 


单个参数的功效

只是为了展示 getIntervals 的灵活性,这就是将资源的间隔长度更改为 75 分钟(而不是30分钟)时的样子.

 //使用相同的"availability"输入数据和75分钟的间隔长度let availableDates = getIntervalDates(getIntervals(75)(可用性))console.log(availableDates) 

输出—每个间隔为75分钟,从75分钟的间隔标记开始

  [{from:Sun Aug 07 2016 21:15:00 GMT-0400(EDT),to:Sun Aug 07 2016 22:29:00 GMT-0400(EDT)},{从:2016年8月7日,星期日22:30:00 GMT-0400(EDT),至:2016年8月7日,星期日23:44:00 GMT-0400(EDT)},{from:Sun Aug 07 2016 23:45:00 GMT-0400(EDT),to:Mon Aug 08 2016 00:59:00 GMT-0400(EDT)},{from:Mon Aug 08 2016 01:00:00 GMT-0400(EDT),to:Mon Aug 08 2016 02:14:00 GMT-0400(EDT)},{从:2016年8月10日星期三00:30:00 GMT-0400(EDT),至:: 2016年8月10日星期三01:44:00 GMT-0400(EDT)},{from:2016年8月10日星期三01:45:00 GMT-0400(EDT),至:2016年8月10日星期三02:59:00 GMT-0400(EDT)}] 


替代实现

如果必须在代码中添加注释,则可能是您做错了什么.因此,我在很大程度上依赖注释来使上面的实现中的意图变得很清楚,从而违反了我自己的规则.

相反,此实现使用微小的过程,每个过程都有明显的意图.此外,我使用了 switch 语句,该语句使我们可以对某些循环响应进行逻辑分组,而无需使用巨大的 if 条件.

 //getIntervals :: Number->[Number]->[{从:号码,到:号码}]const getIntervals = n =>可用性=>{让emptyCount = x =>x === 0让fillCount = x =>x + 1 === n让invalidPrev = x =>x ===未定义让invalidMarker = x =>x%n!== 0让非连续=(x,y)=>x + 1!== y让make =(from,to)=>({从到})return Availability.reduce(({{acc,count,prev},m)=> {切换(true){大小写invalidPrev(prev):案例emptyCount(count)&&invalidMarker(上一个):不区分大小写的情况(上一个,下一个):返回{acc,count:0,prev:m}情况fillCount(count):返回{acc:[... acc,make(m-n,prev)],计数:0,prev:m}默认:返回{acc,count:count + 1,prev:m}}},{acc:[],计数:0,上一个:未定义}).acc}//范围::数字->编号->[数字]const range = min =>max =>{let loop =(res,n)=>n === max?res:循环([... res,n],n + 1)返回循环([],最小值)}//创建样本数据让可用性= [... range(55)(400),//[55,56,57,...,399]... range(3111)(3333),//[3111,3112,3113,...,3332]... range(8888)(9000)//[8888,8889,8890,...,8999]]//获取间隔console.log(getIntervals(30)(可用性)) 

现在您可以看到循环仅以以下三种方式之一进行响应.

  1. 它重置 count 并将 prev 更新为当前分钟
  2. 它将新间隔(使用 make )添加到 acc 并进行 count 重置
  3. 或者,作为默认的情况,它会增加 count 并更新 prev

此外,由于这些过程已被细分为细小部分,因此您可以看到其中的某些过程如何在应用程序的其他区域轻松重用

 //eq :: a->a->布尔const eq = x =>y =>y === x//isZero ::数字->布尔const isZero = eq(0)//isUndefined :: a->布尔const isUndefined = eq(未定义)//isSequential ::数字->编号->布尔const isSequential = x =>eq(x + 1)//isDivisibleBy ::数字->编号->布尔型const isDivisibleBy = x =>y =>(是零)(y%x)//getIntervals :: Number->[Number]->[{从:号码,到:号码}]const getIntervals = n =>可用性=>{让make =(from,to)=>({从到})return Availability.reduce(({{acc,count,prev},m)=> {切换(true){case isUndefined(上一个):案例是零(计数)&&!isDivisibleBy(n)(上一个):案件 !isSequential(上一个)(m):返回{acc,count:0,prev:m}case isSequential(count)(n):返回{acc:[... acc,make(m-n,prev)],计数:0,prev:m}默认:返回{acc,count:count + 1,prev:m}}},{acc:[],计数:0,上一个:未定义}).acc} 

是的,它的工作原理相同.这里的想法是,如果您要多次执行相同的操作,为什么不定义一个过程并使用它呢?绝对没有做到这一点,但是您可以看到通用的小程序如何降低了 getIntervals 的总体复杂性.

现在,无论何时您想检查两个数字是否都是连续的,都可以使用该程序-m isSequential .与 isDivisibleBy —相同您可以轻松地检查 y 是否可以被 x 整除,而无需在应用程序中需要的任何地方重复 y%x === 0 .


不管您信不信,甚至我在这里给您的这些小程序都可以分解为更多部分

 //comp ::(b-> c)->(a-> b)->(a-> c)const comp = f =>g =>x =>f(克(x))//comp2 ::(c-> d)->(a-> b-> c)->(a-> b-> d)const comp2 = comp(comp)(comp)//mod ::数字->编号->数字const mod = x =>y =>y%x//isDivisibleBy ::数字->编号->布尔型const isDivisibleBy = comp2(isZero)(mod) 

最重要的是:不要为之疯狂;知道何时足够的抽象足以解决您的特定问题.只是知道,那里有令人难以置信的技术可以抽象出复杂性.学习技巧并知道何时应用它们.你会很高兴你做了^ _ ^

I have an application displaying a week of bookable time slots (of a resource).

A time slot is always 30 min long and can either start :00 or :30.

The availabilites are internally represented by the week's minutes (in an array), 0 meaning the week's first minute at midnight and 10079 the week's last minute. This means that a resource with 100% availability have 10080 numbers in an array, 0 through 10079.

E.g. the following means that this week has 70 available minutes between 09:55-11:05 on the first day of the week:

[595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665]

How would I calculate and display any possible time slots (i.e. a minimum of 30 consecutive minutes)?

Given the data set above and the current week, and with Monday as first day of the week:

[Mon Aug 08 2016 10:00:00, Mon Aug 08 2016 10:30:00]
[Mon Aug 08 2016 10:30:00, Mon Aug 08 2016 11:00:00]

I don't know if this is easy or not, but I'm currently a bit clueless how to go about doing this in javascript? Any hints are much appreciated!

解决方案

Here's a basic function which takes an interval length, n, and an array of numbers (which represent minutes of availability).

By calling getIntervals (30) (availableMinutes) we'll get all available time slots …

Note, the range procedure below is only used to create sample data. You do not need to include it in your program.

// getIntervals :: Number -> [Number] -> [{from: Number, to: Number}]
const getIntervals = n=> availability=> {
  // intervals computed by reducing availability ...
  let {slots} = availability.reduce(({slots, count, prev}, m)=> {
    // initialize state with first minute
    if (prev === undefined)
      return {slots, count, prev: m}
    // if current interval is empty, we must begin on an interval marker
    else if (count === 0 && prev % n !== 0)
      return {slots, count, prev: m}
    // if current minute is non-sequential, restart search for next interval
    else if (prev + 1 !== m)
      return {slots, count: 0, prev: m}
    // if current interval is complete, concat valid interval
    else if (count === n - 1)
      return {slots: [...slots, {from: m - n, to: prev}], count: 0, prev: m}
    // otherwise, current minute is sequential, add to current interval
    else
      return {slots, count: count + 1, prev: m}
  }, {slots: [], count: 0, prev: undefined})
  // return `slots` value from reduce computation
  return slots
}

// range :: Number -> Number -> [Number]
const range = min=> max=> {
  let loop = (res, n) => n === max ? res : loop([...res, n], n + 1)
  return loop([], min)
}

// create sample data
let availability = [
  ...range (55) (400),     // [55, 56, 57, ..., 399]
  ...range (3111) (3333),  // [3111, 3112, 3113 ,..., 3332]
  ...range (8888) (9000)   // [8888, 8889, 8890, ..., 8999]
]

// get the intervals
console.log(getIntervals (30) (availability))

Just make sure these minute markers are minute offsets of a UTC timestamp and everything will be easy. Displaying these minute markers is just a matter of adding X minutes to a UTC timestamp that is set to the beginning of a resource's week.

We'll create a little procedure here called timestampAddMinutes which takes the minute number and converts it to a Date object based on the resource's timestamp, week

Then, we create a getIntervalDates procedure which applies that to each from and to value of each interval in our array

// timestampAddMinutes :: Date -> Number -> Date
const timestampAddMinutes = t=> m=> {
  let d = new Date(t.getTime())
  d.setMinutes(t.getMinutes() + m)
  return d
}

// getIntervalDates :: [{from: Minute, to: Minute}] -> [{from: Date, to: Date}]
const getIntervalDates = intervals => {
  return intervals.map(({from,to}) => ({
    from: timestampAddMinutes (week) (from),
    to: timestampAddMinutes (week) (to)
  }))
}
  
// sample timestamp for a resource
// Monday at midnight, timezone offset 0
let week = new Date("2016-08-08 00:00:00 +0000")

// interval result from previous code section
let intervals = [
  { from: 60, to: 89 }, { from: 90, to: 119 }, { from: 120, to: 149 },
  { from: 150, to: 179 }, { from: 180, to: 209 }, { from: 210, to: 239 },
  { from: 240, to: 269 }, { from: 270, to: 299 }, { from: 300, to: 329 },
  { from: 330, to: 359 }, { from: 360, to: 389 }, { from: 3120, to: 3149 },
  { from: 3150, to: 3179 }, { from: 3180, to: 3209 }, { from: 3210, to: 3239 },
  { from: 3240, to: 3269 }, { from: 3270, to: 3299 }, { from: 3300, to: 3329 },
  { from: 8910, to: 8939 }, { from: 8940, to: 8969 }
]

// convert intervals to dates
console.log(getIntervalDates(intervals))

You'll see that from and to were each converted to Date objects. Also pay attention to how my browser (in EDT timezone) is automatically converting the UTC timestamp to be displayed in my current timezone. The output above will show them in TZ string format, but they're fully usable Date objects. That means you can call any Date method on them to get specific details like day of week, hour, or minute, etc.

And since our getIntervals function is working properly, you'll also see that each interval is 30-minutes long and starts on :00 or :30

Output

[ { from: Sun Aug 07 2016 21:00:00 GMT-0400 (EDT), to: Sun Aug 07 2016 21:29:00 GMT-0400 (EDT) },
  { from: Sun Aug 07 2016 21:30:00 GMT-0400 (EDT), to: Sun Aug 07 2016 21:59:00 GMT-0400 (EDT) },
  { from: Sun Aug 07 2016 22:00:00 GMT-0400 (EDT), to: Sun Aug 07 2016 22:29:00 GMT-0400 (EDT) },
  { from: Sun Aug 07 2016 22:30:00 GMT-0400 (EDT), to: Sun Aug 07 2016 22:59:00 GMT-0400 (EDT) },
  { from: Sun Aug 07 2016 23:00:00 GMT-0400 (EDT), to: Sun Aug 07 2016 23:29:00 GMT-0400 (EDT) },
  { from: Sun Aug 07 2016 23:30:00 GMT-0400 (EDT), to: Sun Aug 07 2016 23:59:00 GMT-0400 (EDT) },
  { from: Mon Aug 08 2016 00:00:00 GMT-0400 (EDT), to: Mon Aug 08 2016 00:29:00 GMT-0400 (EDT) },
  { from: Mon Aug 08 2016 00:30:00 GMT-0400 (EDT), to: Mon Aug 08 2016 00:59:00 GMT-0400 (EDT) },
  { from: Mon Aug 08 2016 01:00:00 GMT-0400 (EDT), to: Mon Aug 08 2016 01:29:00 GMT-0400 (EDT) },
  { from: Mon Aug 08 2016 01:30:00 GMT-0400 (EDT), to: Mon Aug 08 2016 01:59:00 GMT-0400 (EDT) },
  { from: Mon Aug 08 2016 02:00:00 GMT-0400 (EDT), to: Mon Aug 08 2016 02:29:00 GMT-0400 (EDT) },
  { from: Wed Aug 10 2016 00:00:00 GMT-0400 (EDT), to: Wed Aug 10 2016 00:29:00 GMT-0400 (EDT) },
  { from: Wed Aug 10 2016 00:30:00 GMT-0400 (EDT), to: Wed Aug 10 2016 00:59:00 GMT-0400 (EDT) },
  { from: Wed Aug 10 2016 01:00:00 GMT-0400 (EDT), to: Wed Aug 10 2016 01:29:00 GMT-0400 (EDT) },
  { from: Wed Aug 10 2016 01:30:00 GMT-0400 (EDT), to: Wed Aug 10 2016 01:59:00 GMT-0400 (EDT) },
  { from: Wed Aug 10 2016 02:00:00 GMT-0400 (EDT), to: Wed Aug 10 2016 02:29:00 GMT-0400 (EDT) },
  { from: Wed Aug 10 2016 02:30:00 GMT-0400 (EDT), to: Wed Aug 10 2016 02:59:00 GMT-0400 (EDT) },
  { from: Wed Aug 10 2016 03:00:00 GMT-0400 (EDT), to: Wed Aug 10 2016 03:29:00 GMT-0400 (EDT) },
  { from: Sun Aug 14 2016 00:30:00 GMT-0400 (EDT), to: Sun Aug 14 2016 00:59:00 GMT-0400 (EDT) },
  { from: Sun Aug 14 2016 01:00:00 GMT-0400 (EDT), to: Sun Aug 14 2016 01:29:00 GMT-0400 (EDT) } ]


The power of a single parameter

Just to show the the flexibility of getIntervals, here's what it would look like if you changed a resource's interval length to 75 minutes (instead of 30).

// using the same `availability` input data and a 75-minute interval length
let availableDates = getIntervalDates (getIntervals (75) (availability))

console.log(availableDates)

Output — each interval is 75 minutes long and starts on a starts on a 75-minute interval marker

[ { from: Sun Aug 07 2016 21:15:00 GMT-0400 (EDT), to: Sun Aug 07 2016 22:29:00 GMT-0400 (EDT) },
  { from: Sun Aug 07 2016 22:30:00 GMT-0400 (EDT), to: Sun Aug 07 2016 23:44:00 GMT-0400 (EDT) },
  { from: Sun Aug 07 2016 23:45:00 GMT-0400 (EDT), to: Mon Aug 08 2016 00:59:00 GMT-0400 (EDT) },
  { from: Mon Aug 08 2016 01:00:00 GMT-0400 (EDT), to: Mon Aug 08 2016 02:14:00 GMT-0400 (EDT) },
  { from: Wed Aug 10 2016 00:30:00 GMT-0400 (EDT), to: Wed Aug 10 2016 01:44:00 GMT-0400 (EDT) },
  { from: Wed Aug 10 2016 01:45:00 GMT-0400 (EDT), to: Wed Aug 10 2016 02:59:00 GMT-0400 (EDT) } ]


Alternative Implementation

If you have to put comments in your code, you're probably doing something wrong. So I've broken one of my own rules by heavily relying on comments to make intent clear in the above implementation.

This implementation instead uses tiny procedures which each have immediately apparent intent. Moreover, I used a switch statement which allows us to logically group some of the loop responses without having gigantic if conditions.

// getIntervals :: Number -> [Number] -> [{from: Number, to: Number}]
const getIntervals = n=> availability=> {
  let emptyCount = x=> x === 0
  let filledCount = x=> x + 1 === n
  let invalidPrev = x=> x === undefined
  let invalidMarker = x=> x % n !== 0
  let nonsequential = (x,y)=> x + 1 !== y
  let make = (from,to) => ({from,to})
  return availability.reduce(({acc, count, prev}, m)=> {
    switch (true) {
      case invalidPrev(prev):
      case emptyCount(count) && invalidMarker(prev):
      case nonsequential(prev, m):
        return {acc, count: 0, prev: m}
      case filledCount(count):
        return {acc: [...acc, make(m - n, prev)], count: 0, prev: m}
      default:
        return {acc, count: count + 1, prev: m}
    }
  }, {acc: [], count: 0, prev: undefined}).acc
}

// range :: Number -> Number -> [Number]
const range = min=> max=> {
  let loop = (res, n) => n === max ? res : loop([...res, n], n + 1)
  return loop([], min)
}

// create sample data
let availability = [
  ...range (55) (400),     // [55, 56, 57, ..., 399]
  ...range (3111) (3333),  // [3111, 3112, 3113 ,..., 3332]
  ...range (8888) (9000)   // [8888, 8889, 8890, ..., 8999]
]

// get the intervals
console.log(getIntervals (30) (availability))

Now you can see that the loop only responds in one of 3 ways.

  1. It resets the count and updates prev to the current minute
  2. It appends a new interval (using make) to acc and does a count reset
  3. Or, as adefault case, it increments count and updates prev

Furthermore, now that the procedures have been broken down into tiny pieces, you can see how some of them could be easily reused in other areas of your application

// eq :: a -> a -> Bool
const eq = x=> y=> y === x

// isZero :: Number -> Bool
const isZero = eq (0)

// isUndefined :: a -> Bool
const isUndefined = eq (undefined)

// isSequential :: Number -> Number -> Bool
const isSequential = x=> eq (x + 1)

// isDivisibleBy :: Number -> Number -> Boolean
const isDivisibleBy = x=> y=> (isZero) (y % x)

// getIntervals :: Number -> [Number] -> [{from: Number, to: Number}]
const getIntervals = n=> availability=> {
  let make = (from,to) => ({from,to})
  return availability.reduce(({acc, count, prev}, m)=> {
    switch (true) {
      case isUndefined (prev):
      case isZero (count) && ! isDivisibleBy (n) (prev):
      case ! isSequential (prev) (m):
        return {acc, count: 0, prev: m}
      case isSequential (count) (n):
        return {acc: [...acc, make(m - n, prev)], count: 0, prev: m}
      default:
        return {acc, count: count + 1, prev: m}
    }
  }, {acc: [], count: 0, prev: undefined}).acc
}

And yep, it works the same. The idea here is if you're going to do the same thing more than once, why not define a procedure and use that instead ? By no means do you have to do go this far, but you can see how generic tiny procedures do reduce the overall complexity of getIntervals.

Now any time you want to check if two numbers are sequential, you have a procedure for that — isSequential. Same with isDivisibleBy — you can easily check if y is equally divisible by x without duplicating y % x === 0 everywhere it's needed in your app.


And, believe it or not, even these little procedures I've given you here can be broken down into more parts

// comp :: (b -> c) -> (a -> b) -> (a -> c)
const comp = f=> g=> x=> f (g (x))

// comp2 :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
const comp2 = comp (comp) (comp)

// mod :: Number -> Number -> Number
const mod = x=> y=> y % x

// isDivisibleBy :: Number -> Number -> Boolean
const isDivisibleBy = comp2 (isZero) (mod)

Bottom line is: don't go crazy with it; know when enough abstraction is enough for your particular problem. Just know that there is incredible techniques out there for abstracting complexity away. Learn the techniques and know when to apply them. You'll be happy you did ^_^

这篇关于计算资源的可用性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆