How do I extract the sub arrays and the rest of the array elements from the array?

Let's say I have an array of arrays

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

      

I want to extract arrays and single items as an array as shown below

[[1,2,5,9],[3,4],[6,7,8]] #=>order is not important

      

I tried this but not sure how to handle the individual items

aoa.map{|i|  i if i.kind_of?(Array)}.compact #=> [[3, 4], [6, 7, 8]] 

      

+3


source to share


5 answers


You can use partition (and operator splat

as @CarySwoveland pointed out)



a, i = aoa.partition{ |i| i.is_a? Array }
# => [[[3, 4], [6, 7, 8]], [1, 2, 5, 9]] 
[*a, i]
# => [[3, 4], [6, 7, 8], [1, 2, 5, 9]]

      

+7


source


Enumerable#group_by

returns Hash

which values ​​you want:

aoa.group_by(&:size).values.map(&:flatten)
# => [[1, 2, 5, 9], [3, 4], [6, 7, 8]]

      




@Cary Swoveland pointed out that using size

for grouping is a bad idea, because subarrays with the same size Fixnum#size

will cause unexpected results. group_by(&:class)

should be used instead.

+4


source


nested_a = [[]]
aoa.each {|e| e.is_a?(Array) ? nested_a << e : nested_a[0] << e  }
#remove 1st nested array if empty(Occurs if there were no individual elements)
nested_a.shift if nested_a[0].empty?
nested_a # => [[1, 2, 5, 9], [3, 4], [6, 7, 8]]

      

+2


source


There are three other ways:

aoa = [1, 'cat', [3, 4], 5, [6, 7, 8], 9]

      

1

is_array = ->(e) { Array===e } 
[aoa.reject(&is_array)].concat(aoa.select(&is_array))
  #=> [[1, "cat", 5, 9], [3, 4], [6, 7, 8]]

      

2

Adding a step to using @Doguita Enum # section :

a, e = aoa.partition { |e| Array===e }
[e,*a]
  #=> [[1, "cat", 5, 9], [3, 4], [6, 7, 8]]

      

3

sorted = aoa.sort_by { |e| (Array===e) ? 1 : 0 }
[sorted.shift(aoa.count { |e| !(Array===e) })].concat(sorted)
  #=> [[1, "cat", 9, 5], [6, 7, 8], [3, 4]]

      

What if aoa

only contains arrays?

All of these methods return an array containing an empty array if all elements aoa

are arrays. If you don't want an empty array, press all the way .reject(&:empty?)

. For example:

aoa = [[3, 4], [6, 7, 8]]
[aoa.reject(&is_array)].concat(aoa.select(&is_array))
  #=> [[], [3, 4], [6, 7, 8]]
[aoa.reject(&is_array)].concat(aoa.select(&is_array)).reject(&:empty?)
  #=> [[3, 4], [6, 7, 8]] 

aoa = [1, 'cat', 5, 9]
[aoa.reject(&is_array)].concat(aoa.select(&is_array))
  #=> [[1, "cat", 5, 9]]
[aoa.reject(&is_array)].concat(aoa.select(&is_array)).reject(&:empty?)
  #=> [[1, "cat", 5, 9]]

      

You can use instead reject!

, but if you do, avoid the trap!

You can replace reject(&:empty?)

with reject!(&:empty?)

, which is a little more efficient, but if you do, remember what it reject!

returns nil

if no changes are made, so you need to write:

 aoa = [1, 'cat', 5, 9]
 arr = [aoa.reject(&is_array)].concat(aoa.select(&is_array))
   #=> [[1, "cat", 5, 9]]
 arr.reject!(&:empty?)
   #=> nil
 arr
   #=> [[1, "cat", 5, 9]]

      

+2


source


 >   aoa.inject([[]]) {|temp, x| x.is_a?(Array) ? temp << x : (temp.first << x); temp }
 #=> [[1, 2, 5, 9], [3, 4], [6, 7, 8]]

      

+1


source