Ruby:将日期作为哈希中的键进行比较

时间:2018-01-21 20:44:02

标签: ruby hash key comparison

我的代码使用来自MySQL数据库的信息来生成代表名单/ rota的哈希:

hash = {DateObject1 => "Fender", DateObject2 => "Leyla", DateObject3 => "Eiffel", DateObject4 => "Minkowski", DateObject5 => "Fender", DateObject6 => "Eiffel" etc.}

其中DateObjects是类Date,名称是String。 然后,我有一系列名称,例如

itemarray = ["Eiffel", "Fender", "Leyla", "Minkowski", etc ]

我运行此代码,查找与给定名称关联的所有日期,并检测条件,例如两个结果日期在指定距离内。

class RotaIteration <
Struct.new(:whichiteration, :maxShifts1, :weekdays_too_close1, :weekends_too_close1, :weekday_Saturday_too_close1,
:shifts_too_close1, :max14days, :messes1, :iteration_cost1, :rotamd51)
end

def days_prox(date1,date2) # count all dates between 2 dates, not including    start date
all_days=0
date = date2
while date > date1
  all_days = all_days + 1
  date = date-1;
end
all_days
end

itemarray.each do |item| $q1=hash.find_all {|k,v| v == item)    
callsnum = $q1.length
           if callsnum !=0
              if callsnum > max_totShifts_90d then
                no_solution[:maxShifts] +=1
                no_solution[:iteration_cost] += max_ShiftCost
              end
              until item == callsnum
                d1 = $q1[item-1][0]
                d2 = $q1[item][0]
                d1_daynum = d1.cwday
                d2_daynum = d2.cwday
                proximity = days_prox(d1,d2)
                if proximity < min_any_prox then
                  no_solution[:shifts_too_close] +=1
                  no_solution[:iteration_cost] += shiftTooCloseCost
                  item +=1
                elsif proximity < min_weekEnd_prox and (((6..7) === d1_daynum) and ((5..7) === d2_daynum)) then
                  no_solution[:weekends_too_close] +=1
                  no_solution[:iteration_cost] += weekendTooCloseCost
                  item +=1
                elsif (3..7) === proximity and d1_daynum <=5 and d2_daynum == 6 then
                  no_solution[:weekday_Saturday_too_close] +=1
                  no_solution[:iteration_cost] += weekdaySatTooCloseCost
                  item +=1
                elsif proximity < min_weekDay_prox and d1_daynum <=5 and d2_daynum <=5 then
                  no_solution[:weekdays_too_close] +=1
                  no_solution[:iteration_cost] += weekdayTooCloseCost
                  item +=1
                else
                  item +=1
                end
              end
            end
          end 

所以我可以根据它所具有的“问题”的数量以及这些特定问题的相对“成本”来比较给定的名册。

一旦我有几个名册模式,我可以做到:

rotaIt = RotaIteration.new
        rotaIt.whichiteration = rota_iteration
        rotaIt.maxShifts1 = no_solution[:maxShifts]
        rotaIt.weekdays_too_close1 = no_solution[:weekdays_too_close]
        rotaIt.weekends_too_close1 = no_solution[:weekends_too_close]
        rotaIt.shifts_too_close1 = no_solution[:shifts_too_close]
        rotaIt.messes1 = no_solution[:messes]
        rotaIt.iteration_cost1 = no_solution[:iteration_cost]
        rotaIt.rotamd51 = no_solution[:rotamd5]
        rotaIterations.push(rotaIt) # array

然后我可以根据rotaIterations类的属性对RotaIterations数组进行排序。

我想要实现的目标是什么?

(1)查找与给定名称关联的所有日期 (2)检测条件,例如两个结果日期在指定距离内(并且与3或4个日期类似)

对马克·托马斯和其他人的第一篇文章表示道歉,这一点不明确,并且他们耐心地解开我的问题。

1 个答案:

答案 0 :(得分:1)

最小化您最终编写的代码量并将自己绘制到复杂性角落的一种方法是将事物分解为对象。让我们从一个名册对象开始:

require 'date'

class Roster
  attr_accessor :shifts

  def initialize(hash={})
    @shifts = hash
  end

  def add(date, name)
    raise "Date already used" if shifts[date]
    @shifts[date] = name
  end

  def names
    @shifts.values.uniq
  end

  def shifts_for(name)
    @shifts.select{|_,v| v==name}.keys
  end
end

现在您可以这样使用它:

roster = Roster.new( {Date.today => 'Mark', Date.today + 1 => 'busfender'} )
roster.add(Date.today + 2, 'Mark')

roster.names #=> ['Mark', 'busfender']

roster.shifts_for('Mark') #=> [#<Date: 2018-01-21>, #<Date: 2018-01-23>]

roster.shifts_for('Mark').map(&:wday) #=> [0,2]

roster.shifts_for('Mark').any?(&:sunday?) #=> true

对于您想要执行的所有测试,范围有点大,但如果您在实施测试时遇到任何具体问题,则可以使用minimal, complete, and verifiable example提出单独的SO问题。

修改:我会帮助您处理第二个请求,确定在给定时间段内有多少请求。

假设您使用上面的Roster类,您可以执行以下操作:

diffs = roster.shifts_for('Claire').sort.each_cons(2).to_a.map{|(a,b)| (b-a).to_i}
#=> [2,3,2,4,5,2,5]

这为您提供了克莱尔每天班次变化之间的差异。 (键是each_cons(2),它将每个数组元素与下一个数组元素组合在一起。)因此,为了检查一周内是否有3个或更多个班次,您可以看到是否有任何3个跨度组,总计为7个或更少:

diffs.each_cons(3).map(&:sum).select{|span| span <= 7}.any?

如果任意三班组在7天的范围内,这将返回true

编辑2 :我刚刚注意到您的编辑声明您在MySQL中拥有此信息。几乎所有上述逻辑都应该在数据库中完成(可能使用ActiveRecord,DataMapper或续集)。通过提取到散列并执行散列操作,您将降低其效率并需要更多代码。