测试星期几名称数组是否正确排序?

时间:2015-02-04 16:57:35

标签: ruby

所以我试图测试一周工作日名称数组是否正确排序,无论它从何处开始如此

[Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday] => true
[Thursday, Friday, Saturday, Sunday, Monday, Tuesday, Wednesday] => true
[Monday, Thursday, Tuesday, Wednesday, Sunday, Friday, Saturday] => false

这是我到目前为止所做的:

module Enumerable
  def sorted_by?
    each_cons(2).all? { |a, b| ((yield a) <=> (yield b)) <= 0 }
  end
end

correct_week_days = {
  :monday => 0,
  :tuesday => 1,
  :wednesday => 2,
  :thursday => 3,
  :friday => 4,
  :saturday => 5,
  :sunday => 6
}

test_week_days_array.sorted_by? { |k, v| correct_week_days[k.to_sym] }

在未排序的工作日字符串数组中返回true。我知道这种方法存在缺陷,因为它不能解释循环。

  1. 有办法吗?
  2. 为什么它会返回真实?
  3. 修改

    我应该为我测试的数据提供一个实际的例子

    ['monday', 'thursday', 'tuesday', 'wednesday', 'sunday', 'friday', 'saturday']
    

    这将是一个失败的例子。

    ['wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'monday', 'tuesday']
    

    这会过去。

6 个答案:

答案 0 :(得分:4)

一种简单的双线算法,(可选)适用于包含少于七天的输入。

了解日子:

days = %w(monday tuesday wednesday thursday friday saturday sunday)
# => ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]

将数组加倍,使其包含所有可能的有效子数组:

days *= 2
# => ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]

鉴于您的输入案例......

case1 = %w(monday tuesday wednesday thursday friday saturday sunday)
case2 = %w(thursday friday saturday sunday monday tuesday wednesday)
case3 = %w(monday thursday tuesday wednesday sunday friday saturday)
case4 = %w(wednesday thursday friday saturday sunday monday tuesday)

对于每个输入案例,请查看days数组中是否有与您的输入匹配的slice of consecutive elements

days.each_cons(case1.length).include?(case1) # true
days.each_cons(case2.length).include?(case2) # true
days.each_cons(case3.length).include?(case3) # false
days.each_cons(case4.length).include?(case4) # true

总而言之,这是一个双线解决方案:

def days_in_order?(days)
  search_list = %w(monday tuesday wednesday thursday friday saturday sunday) * 2
  search_list.each_cons(days.length).include?(days)
end

您需要处理输入的大小写不匹配的情况('monday' vs 'Monday'),但这是一个微不足道的扩展。如果您希望此选项仅接受七天的输入数组,请将each_cons(days.length)更改为each_cons(7)

答案 1 :(得分:2)

某些功能方法怎么样?考虑下面的代码(请注意它可以用更紧凑的方式编写,为了便于阅读,我留下了大if-than-else):

DAYS = [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday]

def days_sorted_ok?(weekdays)
  if weekdays.empty? || weekdays.count == 1
    # only one day, so it is sorted well
    return true
  else
    # true, if the next day (weekdays[1]) is the next on the DAYS list
    if weekdays[1] == DAYS[(DAYS.index(weekdays[0]) + 1) % DAYS.count]
      if weekdays.count == 2 
        # only two days in the table, so the whole thing is sorted well
        return true
      else
        # check if the tail of the array is sorted well
        days_sorted_ok?(weekdays[1..-1])
      end
    else
      # second element is not a next weekday
      return false
    end
  end
end

这是一种反复出现的方法。首先,它检查数组是否为空或仅包含一个元素。在这种情况下,它将它视为一种良好的排序方式。在另一种情况下,它检查列表的第二个元素是否是DAYS列表的下一个(使用模数,以循环工作日)。如果是,并且列表只包含2个元素,则表示它排序良好。在另一种情况下,它将在列表的其余部分中搜索递归

以下几项测试:

days_sorted_ok? DAYS
#=> true
days_sorted_ok? [:tuesday, :wednesday, :thursday]
#=> true
days_sorted_ok? [:monday, :tuesday, :wednesday, :thursday, :friday, :sunday, :sunday]
#=> false
days_sorted_ok? [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday, :monday, :sunday]
#=> false

答案 2 :(得分:1)

def same_order?(arr1, arr2)
  raise 'Not same array' unless arr1.sort == arr2.sort

  arr1.length.times do
    arr1 = arr1.rotate
    return true if arr1 == arr2
  end

  return false
end


correct_order = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
a = ['wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'monday', 'tuesday']
b = ['monday', 'thursday', 'tuesday', 'wednesday', 'sunday', 'friday', 'saturday']
p same_order?(correct_order, a) #=> true
p same_order?(correct_order, b) #=> false

答案 3 :(得分:0)

我的解决方案重新排序测试中的数组,并与正确排序的数组进行比较(以及一些数据管理)

def test a
  # deal with an array of symbols or strings
  a = a.map do |day| day.downcase.to_sym end
  # make sure we at least have a monday in the array so we don't get stuck
  if a.include? :monday
    # reorder array so that monday is the first element
    while a.first != :monday
      a << a.shift
    end 
  end 
  # does it match an ideal ordered array?
  a == [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday]
end

答案 4 :(得分:0)

另一种方法是使用方法Array#rotate!(或rotate)。

我假设要测试的数组包含一周中七天的名称,大写,而没有别的。我们只是检查订单是否正确。

首先,如果读者不熟悉使用%w构造字符串数组,那么这里有一个例子:

%w{ Monday Tuesday Wednesday Thursday Friday Saturday Sunday }
  #=> ["Monday", "Tuesday", "Wednesday", "Thursday",
       "Friday", "Saturday", "Sunday"] 

<强>代码

def dow_sorted?(arr)
  %w{ Monday Tuesday Wednesday Thursday Friday Saturday Sunday }
    .rotate!(-arr.index('Monday')) == arr
end

<强>实施例

dow_sorted? %w{ Monday Tuesday Wednesday Thursday Friday Saturday Sunday }
  #=> true
dow_sorted? %w{ Thursday Friday Saturday Sunday Monday Tuesday Wednesday }
  #=> true
dow_sorted? %w{ Monday Thursday Tuesday Wednesday Sunday Friday Saturday }
  #=> false
dow_sorted? %w{ Friday Saturday Sunday Monday Wednesday Tuesday Thursday }
  #=> false

<强>解释

最后一个例子:

arr = %w{ Friday Saturday Sunday Monday Wednesday Tuesday Thursday }
  #=> ["Friday", "Saturday", "Sunday", "Monday",
  #    "Wednesday", "Tuesday", "Thursday"] 

ord = %w{ Monday Tuesday Wednesday Thursday Friday Saturday Sunday }
  #=> ["Monday", "Tuesday", "Wednesday", "Thursday",
  #    "Friday", "Saturday", "Sunday"] 

n = arr.index('Monday')
  #=> 3

a = ord.rotate(-3)
  #=> ["Friday", "Saturday", "Sunday", "Monday",
  #    "Tuesday", "Wednesday", "Thursday"] 

a == arr
  #=> false

答案 5 :(得分:0)

这是另一种解决方案,只是为了将更多算法收集在一起。简而言之 - 你找到一个星期一的位置并将它和所有连续元素移动到数组的开头(实际上它就像使用rotate但没有rotate)然后进行比较使用正确排序的列表。

days = %w(monday tuesday wednesday thursday friday saturday sunday)

case1 = %w(monday tuesday wednesday thursday friday saturday sunday)
case2 = %w(thursday friday saturday sunday monday tuesday wednesday)
case3 = %w(monday thursday tuesday wednesday sunday friday saturday)
case4 = %w(wednesday thursday friday saturday sunday monday tuesday)

def days_in_order?(days)
  monday_index = days.index('monday')
  search_list = %w(monday tuesday wednesday thursday friday saturday sunday)
  search_list == days.slice(monday_index, days.size) | days[0...monday_index]
end

days_in_order?(case1) # true
days_in_order?(case2) # true
days_in_order?(case3) # false
days_in_order?(case4) # true

仅供参考,这个算法比@meagar的算法更快(我的机器上的约4s vs~6s,对于case3的1000000次迭代),但是他的解决方案更容易阅读,我认为在这种情况下性能不是目标: - )

PS。我在days.size而不是days.slice(monday_index, days.size)中写了days.size - monday_index,因为实际上并不需要它,因为目标只是达到或超过数组的大小。