Magic First and Last Indicator in a Loop in Ruby/Rails?

前端 未结 16 2350
眼角桃花
眼角桃花 2020-12-08 00:01

Ruby/Rails does lots of cool stuff when it comes to sugar for basic things, and I think there\'s a very common scenario that I was wondering if anyone has done a helper or s

16条回答
  •  春和景丽
    2020-12-08 00:42

    I needed this functionality from time to time, so I crafted a little class for that purpose.

    The latest version is at: https://gist.github.com/3823837

    Sample:

    ("a".."m").to_a.each_pos do |e|
      puts "Char\tfirst?\tlast?\tprev\tnext\twrapped?\tindex\tposition" if e.first?
      print "#{e.item}\t"
      print "#{e.first?}\t"
      print "#{e.last?}\t"
      print "#{e.prev}\t"
      print "#{e.next}\t"
      print "#{e.wrapped?}\t\t"
      print "#{e.index}\t"
      puts  "#{e.position}\t"
    end
    
    # Char  first?  last?  prev  next  wrapped?  index  position
    # a     true    false        b     false     0      1
    # b     false   false  a     c     true      1      2
    # c     false   false  b     d     true      2      3
    # d     false   false  c     e     true      3      4
    # e     false   false  d     f     true      4      5
    # f     false   false  e     g     true      5      6
    # g     false   false  f     h     true      6      7
    # h     false   false  g     i     true      7      8
    # i     false   false  h     j     true      8      9
    # j     false   false  i     k     true      9      10
    # k     false   false  j     l     true      10     11
    # l     false   false  k     m     true      11     12
    # m     false   true   l           false     12     13
    
    
    
    {
      a: "0",
      b: "1",
      c: "2",
      d: "3",
      e: "4",
      f: "5",
      g: "6",
      h: "7",
      i: "8",
      j: "9",
      k: "10",
      l: "11",
      m: "12",
    }.each_pos do |(k, v), e|
      puts "KV\tChar\t\tfirst?\tlast?\tprev\t\tnext\t\twrapped?\tindex\tposition" if e.first?
      print "#{k} => #{v}\t"
      print "#{e.item}\t"
      print "#{e.first?}\t"
      print "#{e.last?}\t"
      print "#{e.prev || "\t"}\t"
      print "#{e.next || "\t"}\t"
      print "#{e.wrapped?}\t\t"
      print "#{e.index}\t"
      puts  "#{e.position}\t"
    end
    
    # KV      Char        first?  last?   prev        next        wrapped?  index position
    # a => 0  [:a, "0"]   true    false               [:b, "1"]   false     0     1
    # b => 1  [:b, "1"]   false   false   [:a, "0"]   [:c, "2"]   true      1     2
    # c => 2  [:c, "2"]   false   false   [:b, "1"]   [:d, "3"]   true      2     3
    # d => 3  [:d, "3"]   false   false   [:c, "2"]   [:e, "4"]   true      3     4
    # e => 4  [:e, "4"]   false   false   [:d, "3"]   [:f, "5"]   true      4     5
    # f => 5  [:f, "5"]   false   false   [:e, "4"]   [:g, "6"]   true      5     6
    # g => 6  [:g, "6"]   false   false   [:f, "5"]   [:h, "7"]   true      6     7
    # h => 7  [:h, "7"]   false   false   [:g, "6"]   [:i, "8"]   true      7     8
    # i => 8  [:i, "8"]   false   false   [:h, "7"]   [:j, "9"]   true      8     9
    # j => 9  [:j, "9"]   false   false   [:i, "8"]   [:k, "10"]  true      9     10
    # k => 10 [:k, "10"]  false   false   [:j, "9"]   [:l, "11"]  true      10    11
    # l => 11 [:l, "11"]  false   false   [:k, "10"]  [:m, "12"]  true      11    12
    # m => 12 [:m, "12"]  false   true    [:l, "11"]              false     12    13
    

    Actual class:

    module Enumerable
      # your each_with_position method
      def each_pos &block
        EachWithPosition.each(self, &block)
      end
    end
    
    class EachWithPosition
      attr_reader :index
    
      class << self
        def each *a, &b
          handler = self.new(*a, :each, &b)
        end
      end
    
      def initialize collection, method, &block
        @index = 0
        @item, @prev, @next = nil
        @collection = collection
        @callback = block
        self.send(method)
      end
    
      def count
        @collection.count
      end
      alias_method :length, :count
      alias_method :size, :count
    
      def rest
        count - position
      end
    
      def first?
        @index == 0
      end
    
      def last?
        @index == (count - 1)
      end
    
      def wrapped?
        !first? && !last?
      end
      alias_method :inner?, :wrapped?
    
      def position
        @index + 1
      end
    
      def prev
        @prev
      end
    
      def next
        @next
      end
    
      def current
        @item
      end
      alias_method :item, :current
      alias_method :value, :current
    
      def call
        if @callback.arity == 1
          @callback.call(self)
        else
          @callback.call(@item, self)
        end
      end
    
      def each
        @collection.each_cons(2) do |e, n|
          @prev = @item
          @item = e
          @next = n
    
          self.call
          @index += 1
    
          # fix cons slice behaviour
          if last?
            @prev, @item, @next = @item, @next, nil
            self.call
            @index += 1
          end
        end
      end
    end
    

提交回复
热议问题