Gambai
Gambai

Reputation: 690

ruby flatten and compact array full of empty arrays

In my ruby project, I have an array of arrays of arrays which is the result of doing some heavy graph traversal

example:

[[], [1, 2, 3, 4, 5], [[], [], [[], [], [34, 54, 23, 45]]]]

I want the final product to be

[[1, 2, 3, 4, 5], [34, 54, 23, 45]]

flatten will leave me with

[1, 2, 3, 4, 5, 34, 54, 23, 45]

so I don't want that

I'm stuck and could use some help on how to do this

so far, I have

class Array
  def flatten_blanks
    each{ |elem| elem.flatten_blanks if elem.is_a?(Array) }
    reject!{ |elem| elem.is_a?(Array) && elem.length < 1 }
    self
  end
end

which is close but leaves me with something like

[[[[[[[1, 2, 3, 4, 5]]]]]]], [[[[[[[34, 54, 23, 45]]]]]]]

Upvotes: 1

Views: 2031

Answers (4)

Cyril Lemaire
Cyril Lemaire

Reputation: 192

Let's call your array arr

arr.map(&:flatten).reject(&:empty?)

To explain a little, you run flatten on the array's elements instead of the array itself, and then delete the empty subarrays.

Upvotes: 2

Cary Swoveland
Cary Swoveland

Reputation: 110675

require 'json'

arr = [[], [1, 2, 3, 4, 5], [[], [], [[], [], [34, 54, 23, 45]]]]

JSON.parse("[#{arr.to_json.scan(/\[[^\[\]]+\]/).join(',')}]")
  #=> [[1, 2, 3, 4, 5], [34, 54, 23, 45]]

A recursive method (similar to mudsie's recursive proc) could also be used.

def recurse(arr)
  arr.each_with_object([]) { |e,a|
    e.first.is_a?(Array) ? a.concat(recurse e) : a << e unless e.empty? }
end

recurse(arr)
  #=> [[1, 2, 3, 4, 5], [34, 54, 23, 45]]

Upvotes: 2

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

squeeze = -> (arr, acc = []) do
  case arr.compact.first
  when NilClass then acc
  when Array
    arr.reject(&:empty?).map { |e| squeeze.(e, acc) }.first
  else acc << arr
  end
end
squeeze.(arr)
#⇒ [[1, 2, 3, 4, 5], [34, 54, 23, 45]]

Upvotes: 9

user3309314
user3309314

Reputation: 2543

For you example it does the trick:

array = [[], [1, 2, 3, 4, 5], [[], [], [[], [], [34, 54, 23, 45]]]]
array.map(&:flatten).delete_if(&:empty?)

It will produce:

[[1, 2, 3, 4, 5], [34, 54, 23, 45]]

Upvotes: 2

Related Questions