【问题标题】:How do you add an array to another array in Ruby and not end up with a multi-dimensional result?如何在 Ruby 中将一个数组添加到另一个数组而不是多维结果?
【发布时间】:2010-12-20 13:35:39
【问题描述】:
somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

我期待

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

【问题讨论】:

  • 值得一说(不是给你悲伤,而是因为它会一次又一次地咬你)你的期望是这里的问题。 Ruby 数组(与 Perl 中的 say 数组不同)不会在这样的上下文中自动展平。这不是错误:这是一项功能。
  • ri Array@flatten! 为什么这个问题得到这么多票?该文档是明确的Array#flatten! 将自我展平到位。如果未进行任何修改(即数组不包含子数组),则返回 nil。
  • 如果问题对用户有用,就会获得支持。最简单的问题获得最多的支持,因为它们对大多数人有用。
  • @yeyo,你不觉得flatten操作是免费的吗?
  • @Konstantin op 不是在寻找替代品或谈论性能问题,op 期待的是他或她没有得到的结果,因为flatten! 不是那样工作的。最后,这个问题反映了一个逻辑问题,而不是一个优化问题。有关更多信息,请参阅下面的 pilcrow 的答案。

标签: ruby arrays multidimensional-array


【解决方案1】:

你有一个可行的想法,但是 #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

【讨论】:

  • 干得好,因为是唯一一个(我能看到的 5 个)真正指出所提供代码有什么问题的人。 +1
  • 使用 push 代替 concat 可以避免创建第三个数组,因此对于大型数组来说这是首选。
  • 我喜欢用星号推动。非常优雅。
  • @phatmann 与Array#concat 的连接不会分配新数组,与Array#+ 的连接会
  • 这个答案唯一缺少的是每种方法的基准比较。 +1!
【解决方案2】:

您可以只使用+ 运算符!

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

【讨论】:

  • 发帖者想知道如何连接到现有数组,而不是创建一个由两个数组合并的新数组。
  • 注意:a+= b 创建一个新数组:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=&gt; [1,2]
  • @kbrock 正确。如果处理 large 数组,您需要查看@pilcrow 描述的push 方法。
  • 记住+= 创建了新对象。在这样的例子中[1, 2].each_with_object([]) { |number, object| object+=number } 空数组[] 将被返回
  • 添加的项必须是数组
【解决方案3】:

最简洁的方法是使用 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 之前自动展平多维数组。

【讨论】:

  • 您通常需要不变性,因此创建一个新数组是一个更好的主意。
  • “你通常想要不变性”是不准确的。在 20 多年的全职软件开发中,我每天都在使用各种数组和集合。有时您会修改现有的数组。有时您需要使用新实例。
【解决方案4】:

适用于 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]

【讨论】:

  • @Ikuty 这是迄今为止我找到的最优雅的解决方案,你能解释一下* 发生了什么吗?
  • @Abhinay plat 运算符将数组分解为元素,从而在最后一行创建一个单维数组。
  • [*a, *b] 对于旧版本的 ruby​​(即 1.8.7)失败。尽管 Ruby 想告诉你它已经过时了,但 RHEL6 仍然得到维护,这使得 Ruby 1.8 成为一个非常重要的目标版本。
  • 我认为这不能证明这个答案得到的 -1 是合理的。 OP 没有提到 ruby​​ 版本,答案中明确提到了 ruby​​ 版本,所以......你想向后兼容 pre alpha 0.0.0.0.1 版本吗?这是一个很好的解决方案,取决于 ruby​​ 版本
  • 只是指出这个答案与非常“相似”的 JavaScript ES6 非常“相似”,您可以在其中执行 [...array1, ...array2],只需记住 ruby​​ 中的 splat 运算符将改为 *...。它更容易记住
【解决方案5】:
a = ["some", "thing"]
b = ["another", "thing"]

b附加到a并将结果存储在a中:

a.push(*b)

a += b

在任何一种情况下,a 都会变成:

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

但在前一种情况下,b 的元素被附加到现有的a 数组中,而在后一种情况下,两个数组连接在一起,结果存储在a 中。

【讨论】:

  • 请注意a.push(*b)a += b 并不完全相同。前者将新元素添加到现有数组中;后者创建一个包含所有元素的新数组并将其分配给a。如果您在任一 append 方法之前执行aa = a 之类的操作将引用保存到a,然后在之后检查aa,您可以看到差异。在前一种情况下,它随着a 的新值而变化,在后一种情况下,它保持不变。
  • 注意:@DaveHartnoll 指出对于each_with_object 的使用等而言极其重要。执行each_with_object([]) { |thing, result| result += [thing] } 将不起作用,而使用push 方法则可以。
【解决方案6】:

这里有两种方式,注意在这种情况下,第一种方式分配一个新数组(转换为 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"]

【讨论】:

  • 最佳答案!
【解决方案7】:

试试这个,它会结合你的数组删除重复项

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

array3 = array1|array2

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

更多文档请查看“Set Union”

【讨论】:

  • 这是一个或,它返回一个没有重复元素的数组,这里是一个例子,说明它可能没有按照他的要求做,第一个数组中的两个“baz”变成了合二为一,并且第二个数组中的“栏”不会被添加。 array1 = ["foo", "bar", "baz", "baz"] array2 = ["foo1", "bar1", "bar"] array3 = array1|array2 array3 # => ["foo", "bar ", "baz", "foo1", "bar1"]
  • 或者更好:array1 |= [ "foo1", "bar1" ] #=&gt; [ "foo", "bar", "foo1", "bar1" ]
【解决方案8】:

(array1 + array2).uniq

这样你首先得到 array1 元素。你不会得到重复的。

【讨论】:

    【解决方案9】:

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

    【讨论】:

    • 我不知道效率,但这适用于 Ruby 1.8。一般来说,[*a] + [*b] 有效
    • 我认为"another" + "thing" 不会按预期工作。
    【解决方案10】:

    详细说明@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)

    【讨论】:

      【解决方案11】:

      只是另一种方式。

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

      【讨论】:

      • flatten 尽可能递归地展平所有内容。甚至嵌套数组。因此,如果somearrayanotherarray 包含嵌套数组,它们也会被展平。这是通常不希望出现的副作用。
      【解决方案12】:

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

      这个问题的一个自然扩展是“如何在 Ruby 中执行二维数组的逐行连接”。当我在谷歌上搜索“ruby concatenate matrices”时,这个 SO 问题是最重要的结果,所以我想我会把这个(未问但相关的)问题的答案留给后代。


      在某些应用程序中,您可能希望逐行“连接”两个二维数组。类似的,

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

      【讨论】:

        【解决方案13】:

        如果新数据可以是数组或标量,并且如果新数据是数组,您希望防止嵌套新数据,那么 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 
        

        【讨论】:

          【解决方案14】:

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

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

          【讨论】:

          • 非常酷的解决方案!谢谢。
          【解决方案15】:
          a = ['a', 'b']
          b = ['c', 'd']
          arr = [a, b].flatten
          

          这不会删除重复,但是

          a|b
          

          删除重复。

          【讨论】:

          • 注意:这也会递归地展平所有内部数组。
          【解决方案16】:

          somearray = ["some", "thing"]

          anotherarray = [“另一个”,“事物”]

          某个数组 + 另一个数组

          【讨论】:

            【解决方案17】:

            我发现推送或附加数组然后将它们展平更容易,如下所示:

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

            【讨论】:

              【解决方案18】:
              somearray = ["some", "thing"]
              anotherarray = ["another", "thing"]
              somearray + anotherarray # => ["some", "thing", "another", "thing"]
              somearray.concat anotherarray # => ["some", "thing", "another", "thing"]
              somearray.push(anotherarray).flatten # => ["some", "thing", "another", "thing"]
              somearray.push *anotherarray # => ["another", "thing", "another", "thing"]
               
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2017-12-22
                • 2011-07-09
                • 2017-09-17
                • 2012-10-18
                • 1970-01-01
                • 1970-01-01
                • 2012-09-11
                相关资源
                最近更新 更多