通过比较2个相邻元素对数组进行分组

时间:2014-01-09 15:37:10

标签: ruby arrays

我有一个对象数组,我想根据2个相邻元素的属性之间的差异对它们进行分组。该数组已按该属性排序。例如:

原始数组:

array = [a, b, c, d, e]

a.attribute = 1
b.attribute = 3
c.attribute = 6
d.attribute = 9
e.attribute = 10 

如果我想对元素进行分组,使得2个相邻元素的属性之间的差异小于或等于2,则结果应如下所示:

结束结果

result_array = [[a, b], [c], [d, e]]

我有什么

def group_elements_by_difference(array, difference)
    result_array = []
    subgroup = []
    last_element_attribute = array.first.attribute
    array.each do |element|
      if element.attribute <= (last_element_attribute + difference)
        subgroup << element
      else
        #add the subgroup to the result_array
        result_array << subgroup
        subgroup = []
        subgroup << element
      end
      #update last_element_attribute
      last_element_attribute = element.attribute
    end
    result_array << subgroup
end

问题

Ruby 1.9.3中是否有内置函数,例如group_by可以替换我的group_elements_by_difference

3 个答案:

答案 0 :(得分:5)

以下使用数字直接,但算法应与使用属性时的算法相同。它假设所有数字都大于0。如果没有,那么用可行的东西替换它。

array = [1, 3, 6, 9, 10]

[0, *array].each_cons(2).slice_before{|k, l| l - k > 2}.map{|a| a.map(&:last)}
# => [[1, 3], [6], [9, 10]]

使用属性,执行l.attribute等,并将0替换为属性为0的虚拟元素。

答案 1 :(得分:4)

遵循Jan Dvorak的建议,此解决方案使用slice_before和哈希来保持状态:

class GroupByAdjacentDifference < Struct.new(:data)
  def group_by(difference)
    initial = { prev: data.first }

    data.slice_before(initial) do |item, state|
      prev, state[:prev] = state[:prev], item
      value_for(item) - value_for(prev) > difference
    end.to_a
  end

  def value_for(elem)
    elem.attribute
  end
end

require 'rspec/autorun'

describe GroupByAdjacentDifference do

  let(:a) { double("a", attribute: 1) }
  let(:b) { double("b", attribute: 3) }
  let(:c) { double("c", attribute: 6) }
  let(:d) { double("d", attribute: 9) }
  let(:e) { double("e", attribute: 10) }

  let(:data) { [a, b, c, d, e] }
  let(:service) { described_class.new(data) }

  context "#group_by" do
    it "groups data by calculating adjacent difference" do
      expect(service.group_by(2)).to eq([[a, b], [c], [d, e]])
    end
  end
end

给出了

$ ruby group_by_adjacent_difference.rb
.

Finished in 0.0048 seconds
1 example, 0 failures

另外,局部变量也可以用来保持状态,虽然我觉得它有点难以阅读:

class GroupByAdjacentDifference < Struct.new(:data)
  def group_by(difference)
    tmp = data.first

    data.slice_before do |item|
      tmp, prev = item, tmp
      value_for(item) - value_for(prev) > difference
    end.to_a
  end

  def value_for(elem)
    elem.attribute
  end
end

答案 2 :(得分:1)

array = [1, 3, 6, 9, 10]
prev = array[0]
p array.slice_before{|el| prev,el = el,prev; prev-el > 2}.to_a

# => [[1, 3], [6], [9, 10]]