Search code examples
rubyenumerableslice

How to implement slice_after (or group certain elements with certain subsequent ones) in Ruby?


The Enumerable#slice_before method is quite useful, and it does exactly what it says on the tin - slice an array before an element if a certain condition on the element is met. For example, I am using it to group certain numbers to the following ones.

In my case, the IDs 0xF0 to 0xFB should be grouped with the IDs that come after them, including multiple of these IDs in a row (they are "modifier" flags in the thing I'm making). I'm doing it like this:

# example code (for SSCCE)
code = '00FF1234F0AAF0BBF0CCCCF3F4F5AAAAAA'.split('').each_slice(2).map{|n| n.join.to_i 16 }

# grouping the modifiers (more may be added later, so array is used)
code = code.slice_before {|tkn| ![*0xF0..0xFB].include? tkn }.to_a

The result of code after this is

[[0], [255], [18], [52, 240], [170, 240], [187, 240], [204], [204, 243, 244, 245], [170], [170], [170]]

However, the desired result is

[[0], [255], [18], [52], [240, 170], [240, 187], [240, 204], [204], [243, 244, 245, 170], [170], [170]]

I found this entry on bugs.ruby-lang.org, and the response was

The main reason [that this is not implemented] is no one requested.

I have not enough time to implement it now.

Therefore, how can I implement it myself?


Solution

  • It's not the elegant one-liner I'd like to have, but this gets the job done :)

    target = []
    code.each do |i|
      # if there is a previous element and it was one of the "modifiers"
      if target.last && [*0xF0..0xFB].include?(target.last.last)
        # append it to the current subarray
        target.last << i
      else
        # otherwise, append a new subarray
        target << [i]
      end
    end
    

    You'll find the desired array in target with code being unchanged.