【问题标题】:Ruby / Rails slice array base on range of values and priorityRuby / Rails切片数组基于值范围和优先级
【发布时间】:2015-04-03 00:54:48
【问题描述】:

我有一个哈希数组

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

但是它不起作用,当我不需要时,我可能会将事情弄平,而且对于大型数据集可能会很慢。

如果您能提供任何帮助,那就太好了。

【问题讨论】:

  • Ryan,您需要退后一步,用语言说明您的最终目标是什么以及您选择采取的方法。现在你只是列出了一系列步骤,而没有告诉读者你要去哪里以及为什么。这有点像我告诉某人上车,开四个街区,左转,开六个街区,右转......,而不告诉他们他们的目的地或我选择那条特定路线的原因。

标签: ruby-on-rails ruby arrays


【解决方案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 小时制的问题。

【讨论】:

    【解决方案2】:

    这只是部分答案。我将建议您如何将优先级添加到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]}]        
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多