Grouping an array by comparing 2 adjacent elements

后端 未结 3 1501
难免孤独
难免孤独 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 05:45
    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]]
    
    0 讨论(0)
  • 2021-01-21 06:01

    The following uses numerals directly, but the algorithm should be the same as when you do it with attributes. It assumes that all numerals are greater than 0. If not, then replace it with something that works.

    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]]
    

    With attributes, do l.attribute, etc., and replace 0 with a dummy element whose attribute is 0.

    0 讨论(0)
  • 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
    
    0 讨论(0)
提交回复
热议问题