如何从数组中提取子数组和剩余元素作为数组?

3

假设我有一个数组的数组

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

我想要提取数组和类似下面的单个元素数组。
[[1,2,5,9],[3,4],[6,7,8]] #=>order is not important

我尝试过这个,但不确定如何处理单个元素。
aoa.map{|i|  i if i.kind_of?(Array)}.compact #=> [[3, 4], [6, 7, 8]] 
5个回答

7

您可以使用partition(以及由@CarySwoveland指出的splat运算符)。

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]]

我更喜欢这个解决方案。它更直接,因为它不依赖于按大小分组的副作用。 - Mark Thomas
关于您的编辑,如果aoa包含除整数和数组之外的元素怎么办?最好写成!i.is_a?(Array)。我认为在这里使用partition是正确的方法,我认为这就是读者喜欢您的答案的原因。您的“编辑”是一个干扰因素。我会删除它,并添加额外的步骤来修改partition返回的数组。 - Cary Swoveland
@CarySwoveland 同意,但我保留了数组检查,因为问题中的示例仅使用整数。谢谢。 - Doguita

4

Enumerable#group_by返回一个Hash,其值是您所需的内容:

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

@Cary Swoveland指出,使用size进行分组是一个坏主意,因为与Fixnum#size相同大小的子数组会导致意外结果。应该使用group_by(&:class)代替。


2
我会添加 .map(&:flatten) 来满足问题所需的格式。 - Andrey Deineko
2
几乎相同,只是 group_by(&:class) - Yevgeniy Anfilofyev
@YevgeniyAnfilofyev 您的解决方案遵循OP的话,而我的解决方案则遵循问题中的示例。我猜您是正确的,因为这就是被接受的答案所做的。不过我现在还会保持原样。 - Yu Hao
@CarySwoveland 结果不是 [[1, 2], [1, 2, 3, 4, 5, 6, 7, 8]] 吗? - Yu Hao
1.size #=> 8; 2.size #=> 8; [1,2,3,4,5,6,7,8].size #=> 8,这就是我测试后得到的结果。Fixnum.size 返回字节数;Array#size 返回元素数量。 - Cary Swoveland
显示剩余2条评论

2
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]]

1
有趣!你可以考虑使用 nested_a = aoa.each_with_object([[]]) { |e,arr|...。另外,我建议你删除最后一行(注释)中的 #,以提醒读者那里需要 nested_a - Cary Swoveland

2
以下是三种其他方式,供参考:
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

在@Doguita的使用Enumerable#partition的基础上添加一个步骤:

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]]

如果 aoa 只包含数组怎么办?

如果 aoa 的所有元素都是数组,那么这些方法将返回一个包含空数组的数组。如果不想要空数组,则在末尾添加 .reject(&:empty?)。例如:

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]]

你可以使用 reject! 来代替,但是如果这么做要避免陷阱!

你可以将 reject(&:empty?) 替换为更高效的 reject!(&:empty?),但是需要注意的是,reject! 如果没有进行任何更改,则会返回 nil,因此你需要编写以下代码:

 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]]

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接