Ruby / Rails切片数组基于值和优先级的范围

时间:2015-04-03 00:54:48

标签: ruby-on-rails ruby arrays

我有一系列哈希

a = [
    {start_time: 9am, end_time: 10am},
    {start_time: 10am, end_time: 11am},
    {start_time: 11am, end_time: 12am},
    {start_time: 1pm, end_time: 2pm},
    {start_time: 2pm, end_time: 3pm},
    {start_time: 3pm, end_time: 4pm},
    {start_time: 4pm, end_time: 5pm},
    {start_time: 5pm, end_time: 6pm}
    ]

另一组日期范围和优先级 - 1是最高优先级

p = [
    {start_time: 11am, end_time: 3pm, priority: 1},
    {start_time: 2pm, end_time: 5pm, priority: 2},
    {start_time: 9am, end_time: 6m, priority: 3}
    ]

因此,如果将此应用于上一个数组

a = [
    {start_time: 9am, end_time: 10am}, #has priority 3
    {start_time: 10am, end_time: 11am}, #has priority 3
    {start_time: 11am, end_time: 12am}, #has priority 3 & 1
    {start_time: 1pm, end_time: 2pm}, #has priority 3 & 1
    {start_time: 2pm, end_time: 3pm}, #has priority 3 & 2 & 1
    {start_time: 3pm, end_time: 4pm}, #has priority 3 & 2
    {start_time: 4pm, end_time: 5pm}, #has priority 3 & 2
    {start_time: 5pm, end_time: 6pm} #has priority 3
    ]

我想根据优先级在每个范围切片数组。因此,步骤1将以优先级1切片数组:

a = [
    {start_time: 9am, end_time: 10am}, 
    {start_time: 10am, end_time: 11am}, 
    [{start_time: 11am, end_time: 12am}, #priority 1
    {start_time: 1pm, end_time: 2pm}, #priority 1
    {start_time: 2pm, end_time: 3pm}], #priority 1
    {start_time: 3pm, end_time: 4pm}, 
    {start_time: 4pm, end_time: 5pm}, 
    {start_time: 5pm, end_time: 6pm} 
    ]

优先级2不能在起点切片,因为它由优先级1拥有。因此需要最早的起点:

a = [
    {start_time: 9am, end_time: 10am}, 
    {start_time: 10am, end_time: 11am}, 
    [{start_time: 11am, end_time: 12am}, #priority 1
    {start_time: 1pm, end_time: 2pm}, #priority 1
    {start_time: 2pm, end_time: 3pm}], #priority 1
    [{start_time: 3pm, end_time: 4pm}, #priority 2
    {start_time: 4pm, end_time: 5pm}], #priority 2
    {start_time: 5pm, end_time: 6pm} 
    ]

余数变为优先级3,如果start_time与之前的end_time不匹配,我想拆分数组。所以我之后的最终结果是:

a = [
    [{start_time: 9am, end_time: 10am},  #priority 3
    {start_time: 10am, end_time: 11am}], #priority 3
    [{start_time: 11am, end_time: 12am}], #priority 1 - note times aren't consecutive here
    [{start_time: 1pm, end_time: 2pm}, #priority 1
    {start_time: 2pm, end_time: 3pm}], #priority 1
    [{start_time: 3pm, end_time: 4pm}, #priority 2
    {start_time: 4pm, end_time: 5pm}], #priority 2
    [{start_time: 5pm, end_time: 6pm}] #priority 3
    ]

我试图为此构建算法。它基本上按顺序遍历每个优先级,从范围中选择并将其移动到新数组。

p_blocks = []
    p.sort_by! { |x| x.[:priority] }
    p.each do |block|
        f = getReach(a, block[:start_time], block[:end_time])
        f = x.slice_when { |x, y| x[:end_time] != y[:start_time]}.to_a if f.length > 0
        p_blocks << f.flatten
        a-f # remove from array
    end

    return p_blocks

def getReach(logs, start_time, end_time)
    reach = logs.select { |time_log| # get all logs within range  
        ((time_log[:start_time] < end_time and time_log[:start_time] > start_time) or
        (time_log[:end_time] > start_time and time_log[:end_time] < end_time))
        }.sort_by! { |x| x[:start_time] }
    return reach
end

然而,它并不起作用,而且在我不需要的时候,我可能会把事情弄平,而且对于大型数据集来说它可能会很慢。

如果你能提供任何令人敬畏的帮助。

2 个答案:

答案 0 :(得分:1)

试试这个:

a = [{:start_time=>9, :end_time=>10}, {:start_time=>10, :end_time=>11}, {:start_time=>11, :end_time=>12}, {:start_time=>13, :end_time=>14}, {:start_time=>14, :end_time=>15}, {:start_time=>15, :end_time=>16}, {:start_time=>16, :end_time=>17}, {:start_time=>17, :end_time=>18}] 

p = [{start_time: 11, end_time: 3+12, priority: 1},
    {start_time: 2+12, end_time: 5+12, priority: 2},
    {start_time: 9, end_time: 6+12, priority: 3}
    ]

p.each {|i| a.each {|j| j[:priority] = i[:priority] if (j[:priority].nil?) && (i[:start_time]..i[:end_time]).include?(j[:start_time]) && (i[:start_time]..i[:end_time]).include?(j[:end_time])}}

a

输出:

# => [{:start_time=>9, :end_time=>10, :priority=>3}, {:start_time=>10, :end_time=>11, :priority=>3}, {:start_time=>11, :end_time=>12, :priority=>1}, {:start_time=>13, :end_time=>14, :priority=>1}, {:start_time=>14, :end_time=>15, :priority=>1}, {:start_time=>15, :end_time=>16, :priority=>2}, {:start_time=>16, :end_time=>17, :priority=>2}, {:start_time=>17, :end_time=>18, :priority=>3}] 

希望您能处理将上午/下午转换为24小时格式。

答案 1 :(得分:1)

这只是部分答案。我将建议您如何将优先级添加到a,然后切片为优先级1。除此之外,我不明白你在做什么以及你的最终目标是什么。如果问题得到澄清,我或许可以详细说明。

虽然我只回答你的部分问题,但我认为我有一些有用的东西可以说数据结构,代码组织和执行你需要做的一些计算的技巧。

重新组织数据并创建辅助方法

首先,让我们将a置于更方便的形式。请注意,您需要引号中的时间:

a = [
    {start_time:  "9am", end_time: "10am"},
    {start_time: "10am", end_time: "11am"},
    {start_time: "11am", end_time: "12am"},
    {start_time:  "1pm", end_time:  "2pm"},
    {start_time:  "2pm", end_time:  "3pm"},
    {start_time:  "3pm", end_time:  "4pm"},
    {start_time:  "4pm", end_time:  "5pm"},
    {start_time:  "5pm", end_time:  "6pm"}
    ]

执行此操作的两种辅助方法:

def convert(str)
  str.to_i + ((str[-2]=='p') ? 12 : 0)
end

def change(h)
  { interval: convert(h[:start_time])..convert(h[:end_time]) }
end

现在让我们将a转换为更容易使用的结构(不会改变a):

b = a.each_with_object([]) { |h,a| a << change(h) }
  #=> [{:interval=> 9..10}, {:interval=>10..11}, {:interval=>11..12},
  #    {:interval=>13..14}, {:interval=>14..15}, {:interval=>15..16},
  #    {:interval=>16..17}, {:interval=>17..18}]

让我们对p

做同样的事情
p = [
    {start_time: "11am", end_time: "3pm", priority: 1},
    {start_time:  "2pm", end_time: "5pm", priority: 2},
    {start_time:  "9am", end_time: "6pm", priority: 3}
    ]

q = p.each_with_object([]) do |h,a|
  a << change(h).merge(priority: h[:priority])
end
  #=> [{:interval=>11..15, :priority=>1},
  #    {:interval=>14..17, :priority=>2},
  #    {:interval=> 9..18, :priority=>3}] 

让我们定义一个帮助器来确定两个范围是否重叠:

def overlap?(r1,r2)
  !(r1.first >= r2.last || r1.last <= r2.first)
end      

检查:

overlap?( 1..10, 10..20) #=> false
overlap?( 1..11, 10..20) #=> true 
overlap?(12..16, 10..20) #=> true 
overlap?( 1..30, 10..20) #=> true 
overlap?(20..30, 10..20) #=> false 

添加优先级

我们现在可以很容易地为b中的每个哈希添加优先级:

b.each do |h|
  h[:priority] = []
  q.each do |g|
    h[:priority] << g[:priority] if overlap?(h[:interval], g[:interval])
  end      
end     
b #=> [{:interval=> 9..10, :priority=>[3]},
  #    {:interval=>10..11, :priority=>[3]},
  #    {:interval=>11..12, :priority=>[1, 3]},
  #    {:interval=>13..14, :priority=>[1, 3]},
  #    {:interval=>14..15, :priority=>[1, 2, 3]},
  #    {:interval=>15..16, :priority=>[2, 3]},
  #    {:interval=>16..17, :priority=>[2, 3]},
  #    {:interval=>17..18, :priority=>[3]}]

切割优先级为1

在这种特殊情况下,优先级为1的哈希值是连续的,因此我们可以根据您的要求将它们替换为数组。但是,我不知道它们是否必然与其他数据相邻。

c = b.each_with_object([]) do |h,a|
  if h[:priority].include?(1)
    if a.empty? || a.last.is_a?(Hash)
      a << [h]
    else
      a.last << h
    end
  else
    a << h
  end
end
  #=> [ {  :interval=>9..10,  :priority=>[3]},
  #     {  :interval=>10..11, :priority=>[3]},
  #     [ {:interval=>11..12, :priority=>[1, 3]},
  #       {:interval=>13..14, :priority=>[1, 3]},
  #       {:interval=>14..15, :priority=>[1, 2, 3]}],
  #     {  :interval=>15..16, :priority=>[2, 3]},
  #     {  :interval=>16..17, :priority=>[2, 3]},
  #     {  :interval=>17..18, :priority=>[3]}]