3

I have array that looks like

[nil, nil, nil, nil, 5, 6, 7, 8, 9, 10, 11, nil, nil, nil, nil, nil, 17, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 50, 51]

I want to "unflatten" it, so it'd look like

[[5,6,7,8,9,10,11],[17], [50,51]]

What would be the easiest way to achieve that?

Avdept
  • 2,261
  • 2
  • 26
  • 48
  • 2
    @CarySwoveland You're right. Bad move by me. Will wait for more answers and mark most interesting – Avdept Aug 10 '15 at 16:27

3 Answers3

4
arr.slice_before(&:nil?).map(&:compact).reject(&:empty?)

Just out of curiosity, O(N):

arr.inject([[]]) do |memo, e| 
  e ? memo.last << e : (memo << [] unless memo.last.empty?)
  memo
end

Or, with modern ruby goodness:

arr.each_with_object([[]]) do |e, memo| 
  e && memo.last << e || (memo << [] unless memo.last.empty?)
end

And, totally out of curiosity (it’s a joke, please, do not use):

JSON.parse arr.to_json
              .gsub(/\D{2,}/, '],[')
              .gsub(/\A\D+/, '[[')
              .gsub(/\D+\z/, ']]')
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
3

I thought about

a.slice_when { |e1, e2| e1 != e2 && e2.nil? }.map(&:compact)
# => [[5, 6, 7, 8, 9, 10, 11], [17], [50, 51]]
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
  • 1
    I love [Enumerable#slice_when](http://ruby-doc.org/core-2.2.0/Enumerable.html#method-i-slice_when). I believe we got it with Ruby 2.2. – Cary Swoveland Aug 10 '15 at 16:11
  • @CarySwoveland Yes, it is from 2.2.2. – Arup Rakshit Aug 10 '15 at 16:13
  • There is `Enumerable#chunk_while`, which I proposed, coming in Ruby 2.3: https://bugs.ruby-lang.org/issues/10769. – sawa Aug 10 '15 at 17:11
  • 1
    @sawa, that will be a welcome addition. I've been thinking of proposing the addition of a method [Array#difference](http://stackoverflow.com/questions/24987054/how-to-select-unique-elements). Could you give me a link to where you proposed `chunk_by`, so I can see the procedure? – Cary Swoveland Aug 10 '15 at 17:16
  • Cary, I misstyped the name. It is `chunk_while`. The change is [here](http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=50889). Cary and Arup: `chunk_while` is the negation of `slice_when`. – sawa Aug 10 '15 at 17:17
1

We can use Enumerable#chunk for this:

arr = [nil, nil, nil, nil, 5, 6, 7, 8, 9, 10, 11, nil, nil, nil, nil, nil, 17,
       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 50, 51]

arr.chunk(&:nil?).reject(&:first).map(&:last)
  #=> [[5, 6, 7, 8, 9, 10, 11], [17], [50, 51]] 

The steps:

enum = arr.chunk(&:nil?)
  #=> #<Enumerator: #<Enumerator::Generator:0x007fd303042440>:each> 

We can examine the elements of the enumerator enum by converting it to an array:

enum.to_a
  #=> [[true,  [nil, nil, nil, nil]],
  #    [false, [5, 6, 7, 8, 9, 10, 11]],
  #    [true,  [nil, nil, nil, nil, nil]],
  #    [false, [17]],
  #    [true,  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]],
  #    [false, [50, 51]]] 

Two more steps:

a = enum.reject(&:first)
  #=> [[false, [5, 6, 7, 8, 9, 10, 11]],
  #    [false, [17]],
  #    [false, [50, 51]]] 
a.map(&:last)
  #=> [[5, 6, 7, 8, 9, 10, 11], [17], [50, 51]] 

Edit: Thanks to @Stefan, today I learned something new about the way chunk handles nils. As he suggests, we can simplify this to:

arr.chunk { |e| e && false }.map(&:last)
  #=> [[5, 6, 7, 8, 9, 10, 11], [17], [50, 51]]

Well, maybe that's not exactly what he said, but it works just as well. (My variant is more of a head-scratcher.)

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100