Ruby inject with index and brackets

前端 未结 4 1166
谎友^
谎友^ 2021-02-12 10:34

I try to clean my Code. The first Version uses each_with_index. In the second version I tried to compact the code with the Enumerable.inject_with_index-constr

相关标签:
4条回答
  • 2021-02-12 10:52
    lines = %w(a b c)
    indexes = lines.each_with_index.inject([]) do |acc, (el, ind)|
      acc << ind - 1 if el == "b"
      acc
    end
    
    indexes # => [0]
    
    0 讨论(0)
  • 2021-02-12 10:57

    What is the use of these brackets?

    To understand the brackets, first you need to understand how destruction works in ruby. The simplest example I can think of this this:

    1.8.7 :001 > [[1,3],[2,4]].each do |a,b|
    1.8.7 :002 >     puts a, b
    1.8.7 :003?>   end
    1
    3
    2
    4
    

    You should know how each function works, and that the block receives one parameter. So what happens when you pass two parameters? It takes the first element [1,3] and try to split (destruct) it in two, and the result is a=1 and b=3.

    Now, inject takes two arguments in the block parameter, so it is usually looks like |a,b|. So passing a parameter like |group_container, (element,index)| we are in fact taking the first one as any other, and destructing the second in two others (so, if the second parameter is [1,3], element=1 and index=3). The parenthesis are needed because if we used |group_container, element, index| we would never know if we are destructing the first or the second parameter, so the parenthesis there works as disambiguation.

    9In fact, things works a bit different in the bottom end, but lets hide this for this given question.)

    0 讨论(0)
  • 2021-02-12 10:58

    Seems like there already some answers given with good explanation. I want to add some information regards the clear and readable.

    Instead of the solution you chose, it is also a possibility to extend Enumerable and add this functionality.

    module Enumerable
      # The block parameter is not needed but creates more readable code.
      def inject_with_index(memo = self.first, &block)
        skip = memo.equal?(self.first)
        index = 0
        self.each_entry do |entry|
          if skip
            skip = false
          else
            memo = yield(memo, index, entry)
          end
          index += 1
        end
        memo
      end
    end
    

    This way you can call inject_with_index like so:

    # m = memo, i = index, e = entry
    (1..3).inject_with_index(0) do |m, i, e|
      puts "m: #{m}, i: #{i}, e: #{e}"
      m + i + e
    end
    #=> 9
    

    If you not pass an initial value the first element will be used, thus not executing the block for the first element.

    0 讨论(0)
  • 2021-02-12 11:12

    What is the use of these brackets?

    It's a very nice feature of ruby. I call it "destructuring array assignment", but it probably has an official name too.

    Here's how it works. Let's say you have an array

    arr = [1, 2, 3]
    

    Then you assign this array to a list of names, like this:

    a, b, c = arr
    a # => 1
    b # => 2
    c # => 3
    

    You see, the array was "destructured" into its individual elements. Now, to the each_with_index. As you know, it's like a regular each, but also returns an index. inject doesn't care about all this, it takes input elements and passes them to its block as is. If input element is an array (elem/index pair from each_with_index), then we can either take it apart in the block body

    sorted.each_with_index.inject(groups) do |group_container, pair|
      element, index = pair
    
      # or
      # element = pair[0]
      # index = pair[1]
    
      # rest of your code
    end
    

    Or destructure that array right in the block signature. Parentheses there are necessary to give ruby a hint that this is a single parameter that needs to be split in several.

    Hope this helps.

    0 讨论(0)
提交回复
热议问题