如何在Ruby中将一个数组添加到另一个数组中而又未得到多维结果?


474
somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

我期望

["some","thing","another","thing"]

6
值得一说(不是让您感到悲伤,而是因为它会一次又一次地咬您),您的期望是这里的问题。Ruby数组(不同于Perl中的数组)不会在这样的上下文中自动变平。这不是错误:这是一个功能。
Telemachus,2009年

3
ri Array@flatten!为什么这个问题获得这么多选票?该文档是明确的Array#flatten! Flattens自我。如果未进行任何修改,则返回nil(即数组不包含子数组。)
yeyo

7
如果问题对用户有用,则会受到质疑。最简单的问题得到最多的赞成,因为它们对大多数人有用。
Ziggy

@yeyo,你不只是认为扁平化操作是免费的吗?
康斯坦丁

@Konstantin op并没有寻找替代方案或谈论性能问题,op期望他或她没有得到结果是因为这种方法flatten!不起作用。最后,该问题反映的是逻辑问题,而不是优化问题。有关更多信息,请参见下面的pilcrow的答案。
yeyo

Answers:


713

您已经有了一个可行的想法,但是的#flatten!位置错误-它会使接收器变平,因此可以将其[1, 2, ['foo', 'bar']]变成[1,2,'foo','bar']

我无疑会忘记一些方法,但是您可以串联

a1.concat a2
a1 + a2              # creates a new array, as does a1 += a2

前置/追加

a1.push(*a2)         # note the asterisk
a2.unshift(*a1)      # note the asterisk, and that a2 is the receiver

拼接

a1[a1.length, 0] = a2
a1[a1.length..0] = a2
a1.insert(a1.length, *a2)

追加并展平

(a1 << a2).flatten!  # a call to #flatten instead would return a new array

17
做得很好,因为它是实际上指出了所提供的代码出了什么问题的唯一一个(我能看到的5个)。+1
Mike Woodhouse,2009年

53
使用push而不是concat可以避免创建第三个数组,因此,这对于大型数组是首选的。
phatmann 2012年

8
我喜欢带有星号的按钮。十分优雅。
orourkedd 2014年

14
@phatmann级联与Array#concat不分配一个新的数组,与级联Array#+确实
cbliard

5
这个答案唯一缺少的是每种方法的基准比较。+1!
Terra Ashley 2015年

205

您可以只使用+运算符!

irb(main):001:0> a = [1,2]
=> [1, 2]
irb(main):002:0> b = [3,4]
=> [3, 4]
irb(main):003:0> a + b
=> [1, 2, 3, 4]

您可以在此处阅读有关数组类的所有信息:http : //ruby-doc.org/core/classes/Array.html


15
张贴者想知道如何连接到现有阵列,而不是创建一个由两个阵列结合而成的新阵列。
phatmann 2012年

1
注意:a+= b创建一个新数组:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
kbrock

1
@kbrock正确。如果处理大型数组,则需要查看push@pilcrow描述的方法。
2015年

2
请记住,这+=会创建新对象。在此示例中,将返回[1, 2].each_with_object([]) { |number, object| object+=number }空数组[]
Filip Bartuzi,2015年

1
添加的项目必须是数组
RousseauAlexandre

66

最干净的方法是使用Array#concat方法。它不会创建新的数组(与Array#+一样,但会创建新的数组)。

直接来自文档(http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat):

concat(other_ary)

将other_ary的元素追加到self。

所以

[1,2].concat([3,4])  #=> [1,2,3,4]  

如果将Array#concat作为参数传入,则不会展平多维数组。您需要单独处理:

arr= [3,[4,5]]
arr= arr.flatten   #=> [3,4,5]
[1,2].concat(arr)  #=> [1,2,3,4,5]

最后,您可以使用我们的corelib gem(https://github.com/corlewsolutions/corelib),它可以为Ruby核心类添加有用的帮助器。特别是我们有一个Array#add_all方法,该方法将在执行concat之前自动展平多维数组。


1
通常,您需要不变性,因此创建一个新数组是一个更好的主意。
vasilakisfil 2015年

5
“您通常想要不变性”是不准确的。在20多年的全职软件开发工作中,我每天都处理各种阵列和集合。有时,您在适当位置修改现有阵列。有时您需要使用新实例。
Corlew Solutions

35

适用于Ruby版本> = 2.0但不适用于旧版本的简单方法:

irb(main):001:0> a=[1,2]
=> [1, 2]
irb(main):003:0> b=[3,4]
=> [3, 4]
irb(main):002:0> c=[5,6]
=> [5, 6]
irb(main):004:0> [*a,*b,*c]
=> [1, 2, 3, 4, 5, 6]

2
@Ikuty这是迄今为止我发现的最优雅的解决方案,您能解释一下*这里发生了什么吗?
Abhinay

@ Abhinay,plat运算符将数组分解为元素,从而在最后一行创建一个一维数组。
奥马尔·阿里

[*a, *b]对于旧版本的ruby(即1.8.7)失败。尽管Ruby希望告诉您它已经淘汰了,但RHEL6仍然得以维护,这使Ruby 1.8成为重要的目标版本。
Otheus

1
我不认为这个答案是-1。OP没有提到红宝石版本,答案中没有明确提到红宝石版本,所以...您想向后兼容pre alpha 0.0.0.0.1版本吗?这是很好的解决方案之一,这取决于红宝石版
朱利库季

1
只是指出此答案与您可以执行的非常惯用的JavaScript ES6非常“相似” [...array1, ...array2],只是要记住,使用splatruby 的运算符将*代替...。它使人们更容易记住
sandre89 '18

34

试试这个,它将合并您的数组,删除重复项

array1 = ["foo", "bar"]
array2 = ["foo1", "bar1"]

array3 = array1|array2

http://www.ruby-doc.org/core/classes/Array.html

更多文档请参见“设置联合”


这是一个or,它返回一个没有重复元素的数组,这是一个示例,它可能不执行他所要的操作,第一个数组中的两个“ baz”变成一个,而“ bar”在第二个数组中不会被添加。array1 = [“ foo”,“ bar”,“ baz”,“ baz”] array2 = [“ foo1”,“ bar1”,“ bar”] array3 = array1 | array2 array3#=> [“ foo”,“ bar “,” baz“,” foo1“,” bar1“]
约书亚脸颊

甚至更好:array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
Joshua Pinter

33

这有两种方法,在这种情况下请注意,第一种方法分配一个新数组(转换为somearray = somearray + anotherarray)

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray += anotherarray # => ["some", "thing", "another", "thing"]

somearray = ["some", "thing"]
somearray.concat anotherarray # => ["some", "thing", "another", "thing"]

24
a = ["some", "thing"]
b = ["another", "thing"]

要追加ba并保存结果a

a.push(*b)

要么

a += b

无论哪种情况,都a变为:

["some", "thing", "another", "thing"]

但在前一种情况下,将的元素b附加到现有a数组,而在后一种情况下,将两个数组串联在一起并将结果存储在中a


2
请注意,这a.push(*b)与并不完全相同a += b。前者将新元素添加到现有数组中。后者会创建一个包含所有元素的新数组,并将其分配给a。如果您执行类似aa = a将引用保存到a任何一个append方法之前,然后再进行检查的操作,则可以看到区别aa。在前一种情况下,它随着的新值而变化a,在后一种情况下,它保持不变。
戴夫·哈特诺尔

20

(array1 + array2).uniq

这样,您将首先获得array1元素。您将不会得到任何重复。


9

详细说明@Pilcrow的答案,唯一适用于大型数组的答案是concat+),因为它很快,并且在循环内操作时不会分配要被垃圾回收的新对象。

这是基准:

require 'benchmark'

huge_ary_1 = Array.new(1_000_000) { rand(5_000_000..30_000_00) }

huge_ary_2 = Array.new(1_000_000) { rand(35_000_000..55_000_00) }

Benchmark.bm do |bm|
  p '-------------------CONCAT ----------------'
  bm.report { huge_ary_1.concat(huge_ary_2) }

  p '------------------- PUSH ----------------'
  bm.report { huge_ary_1.push(*huge_ary_2)  }
end

结果:

       user     system      total        real
"-------------------CONCAT ----------------"
  0.000000   0.000000   0.000000 (  0.009388)
"------------------- PUSH ----------------"
  example/array_concat_vs_push.rb:13:in `block (2 levels) in <main>': stack level too deep (SystemStackError)

如您所见,当数组足够大时,使用方法push会引发ERRORstack level too deep (SystemStackError)


8

本质上,问题是“如何在Ruby中连接数组”。自然,答案是使用concat+几乎在每个答案中都提到。

这个问题的自然扩展是“如何在Ruby中执行2D数组的行级联”。当我用谷歌搜索“ ruby​​ concatenate matrixs”时,这个SO问题是最高的结果,所以我想我将对那个(未提出但相关的)问题的答案留在后头。


在某些应用程序中,您可能需要按行“连接”两个2D数组。就像是,

[[a, b], | [[x],    [[a, b, x],
 [c, d]] |  [y]] =>  [c, d, y]]

这有点像“增强”矩阵。例如,我使用此技术创建了一个邻接矩阵,以表示一堆较小的矩阵中的图形。如果没有这项技术,我将不得不以一种容易出错或难以思考的方式迭代这些组件。例如,我可能不得不做一个each_with_index。相反,我将zipflatten组合如下,

# given two multi-dimensional arrays that you want to concatenate row-wise
m1 = [[:a, :b], [:c, :d]]
m2 = [[:x], [:y]]

m1m2 = m1.zip(m2).map(&:flatten)
# => [[:a, :b, :x], [:c, :d, :y]]

8

只是另一种方式。

[somearray, anotherarray].flatten
=> ["some", "thing", "another", "thing"]

flatten递归地尽可能平坦化所有内容。甚至是嵌套数组。因此,如果somearrayanotherarray包含嵌套数组,它们也将被展平。这是通常不希望的副作用。
哈杰罗


5

如果新数据可能是数组或标量,并且您想防止新数据嵌套在数组中,则splat运算符非常棒!它为标量返回标量,并为数组返回解压缩的参数列表。

1.9.3-p551 :020 > a = [1, 2]
 => [1, 2] 
1.9.3-p551 :021 > b = [3, 4]
 => [3, 4] 
1.9.3-p551 :022 > c = 5
 => 5 
1.9.3-p551 :023 > a.object_id
 => 6617020 
1.9.3-p551 :024 > a.push *b
 => [1, 2, 3, 4] 
1.9.3-p551 :025 > a.object_id
 => 6617020 
1.9.3-p551 :026 > a.push *c
 => [1, 2, 3, 4, 5] 
1.9.3-p551 :027 > a.object_id
 => 6617020 

4

我很惊讶没有人提到reduce它,当您有一个数组数组时,它会很好地工作:

lists = [["a", "b"], ["c", "d"]]
flatlist = lists.reduce(:+)  # ["a", "b", "c", "d"]

4
a = ['a', 'b']
b = ['c', 'd']
arr = [a, b].flatten

这不会删除公仔,但是

a|b

去除公仔。


注意:这也递归地展平了所有内部数组。
Mirodinho

2

我发现更容易推送或追加数组,然后将它们展平到位,就像这样:

somearray = ["some", "thing"]
anotherarray = ["another", "thing"]
somearray.push anotherarray # => ["some", "thing", ["another", "thing"]]
#or
somearray << anotherarray # => ["some", "thing", ["another", "thing"]]
somearray.flatten!  # => ["some", "thing", "another", "thing"]
somearray # => ["some", "thing", "another", "thing"]

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.