szymanowski
szymanowski

Reputation: 1379

How do I split an array using nil as delimitor?

I'm searching a method for converting:

[1,2,3,nil,4,5,nil,6,7,8,9] 

into:

[[1,2,3],[4,5],[6,7,8,9]]

Is there a built-in way to do that in Ruby?

Upvotes: 0

Views: 315

Answers (5)

Amey
Amey

Reputation: 8548

I would join the array and then split the array.

a =[1,2,3,nil,4,5,nil,6,7,8,9] 

a = a.join("-").split("--")
a.map! { |a| a.split("-") }
a.map! {|e| e.map! {|f| f.to_i}}
puts a.inspect

#[[1, 2, 3], [4, 5], [6, 7, 8, 9]]

Made edits (based on comments), to make it an integer once again. Still not a good answer though.

Upvotes: 0

lurker
lurker

Reputation: 58234

I found kind of an interesting one. There's probably a way to shorten it:

[1,2,3,nil,4,5,nil,6,7,8,9].chunk {|e| e.nil?}.select {|e| not e[0]}.flatten(1).delete_if {|e| not e}

Upvotes: 1

the Tin Man
the Tin Man

Reputation: 160551

I'd use:

[1,2,3,nil,4,5,nil,6,7,8,9].slice_before{ |e| e.nil? }.map(&:compact)
=> [[1, 2, 3], [4, 5], [6, 7, 8, 9]]

slice_before is really powerful when you want to break an array into chunks, either by searching for a repeating pattern by passing in a regex, or something you can compute via the block. It's much too powerful to summarize right here so take time to read the documentation and play with the examples.

Upvotes: 6

Ju Liu
Ju Liu

Reputation: 3999

This should work:

array = [1,2,3,nil,4,5,nil,6,7,8,9]
array.inject([[]]) do |result, number|
  number ? result.last << number : result << []
  result
end

#=> [[1, 2, 3], [4, 5], [6, 7, 8, 9]]

Explanation time :-)

  • inject starts with an array containing an empty array
  • for each element, it checks if it's nil
  • if it isn't, it appends the current number to the previous array
  • if it is, it creates a new empty array
  • all this while updating result, which is an array of arrays

-- EDIT --

Checking David's reply I checked Rails implementation of this:

def split(value = nil)
  using_block = block_given?

  inject([[]]) do |results, element|
    if (using_block && yield(element)) || (value == element)
      results << []
    else
      results.last << element
    end

    results
  end
end

If you skip the block implementation, it has the exact same structure of my code. Yay! :)

Upvotes: 4

David
David

Reputation: 7303

[1,2,3,nil,4,5,nil,6,7,8,9].split(nil)

Whoops array#split is a Rails method

Upvotes: 2

Related Questions