Search code examples
arraysrubyenumerableenumerator

Ruby Split array for two with partition and with_index


Can you give explanation of logic or algorithm of ruby behaviour for this construction:

arr = [1,2,3,4,5]
arr.partition.with_index{|_,index| index>2} 

How to formalize logic when iterate through Enumerable give 2 arrays output. When we just call single partition its clear - just method behavior, but when it trails by with_index this construction become "Magical" for me.

Thank you

UPD: The condition is not in block of partition, it is in separate 'Enumerable' Object method block. This method is with_index. This second level of interaction is interesting for me. Why do conditions of with_index have influence on partition result? This is a behavior that is not clear from partition documentation.


Solution

  • You've probably read in the Ruby docs for partition:

    If no block is given, an enumerator is returned instead.

    > arr = [1,2,3,4,5]
    => [1, 2, 3, 4, 5]
    > arr.partition
    => #<Enumerator: ...>
    

    You'll see the same thing in the description for a lot of the methods in Enumerable but there isn't much detail about the enumerator that's returned.

    The key to the enumerator chaining is in the the behaviour of the each method in the Enumerator that's returned. For example, the Enumerator returned by partition has an each method that behaves like partition. This is how you're able to pass a single block and get the combined behaviour of partition and with_index. If you do this in a couple of steps it might help:

    > enumerator = arr.partition
    => #<Enumerator: ...>
    > enumerator.each { |n| n < 3 } # just to demonstrate `each` performing a partition
    => [[1, 2], [3, 4, 5]]
    > enumerator.with_index { |_, index| index > 2 }
    => [[4, 5], [1, 2, 3]]
    

    Another way to think about it is that partition with no block given is like passing :partition to enum_for e.g.

    > another_enum = arr.enum_for(:partition)
    => #<Enumerator: ...>
    > another_enum.with_index { |_, index| index > 2 } # same result as above
    => [[4, 5], [1, 2, 3]]