Grouping an array by comparing 2 adjacent elements

后端 未结 3 1500
难免孤独
难免孤独 2021-01-21 05:10

I have an array of objects and I would like to group them based on the difference between the attributes of 2 adjacent elements. The array is already sorted by that attribute. F

3条回答
  •  悲&欢浪女
    2021-01-21 06:03

    Following Jan Dvorak's suggestion, this solution uses slice_before and a hash to keep the state:

    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
    

    which gives

    $ ruby group_by_adjacent_difference.rb
    .
    
    Finished in 0.0048 seconds
    1 example, 0 failures
    

    In alternative, local variables could also be used to keep state, although I find it a bit harder to read:

    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
    

提交回复
热议问题