【问题标题】:Array of arrays merge in Rails数组数组在 Rails 中合并
【发布时间】:2017-05-23 18:39:36
【问题描述】:

我有一个这样的数组:[['Area1', 12345], ['Area2', 54321]]

因此,如果我要将此数组['Area1', 33441] 添加到现有数组中,我想像这样合并数组[['Area1', 12345, 33441], ['Area2', 54321]] 或显示这样的信息:

Area1: 12345, 33441 Area2: 54321

有谁知道这在 Rails 中是否可行?谢谢!

【问题讨论】:

  • 似乎哈希可能是更合适的数据结构。

标签: ruby-on-rails arrays


【解决方案1】:

你可以使用 Hash#merge! 方法和一个块:

initial = [['Area1', 12345], ['Area2', 54321]].to_h
initial.merge!({'Area1' => 1212 }) { |_,v1,v2| [v1,v2].flatten }
#=> {"Area1"=>[12345, 1212], "Area2"=>54321}

如果你希望结果是一个数组,你可以使用

initial.merge!({'Area1' => 1212 }) { |_,v1,v2| [v1,v2].flatten }.to_a

【讨论】:

  • 是的,这非常适合我的情况。非常感谢@xlembouras!
  • 如果"Area2"=>54321"Area2"=>[54321](与两个或多个值的数组保持一致),我希望返回值更容易使用。例如,initial = [['Area1', 12345], ['Area2', 54321]]; (initial + [['Area1', 1212]]).each_with_object({}) { |(area,nbr),h| h.merge!(area=>[nbr]) { |_,v1,v2| v1 + v2 } } #=> {"Area1"=>[12345, 1212], "Area2"=>[54321]}。 (读者:x 的答案和这个变体都使用Hash#merge! 的形式,它使用一个块来计算被合并的两个哈希中存在的键的值。)
【解决方案2】:

您可能没有使用正确的数据结构。您最好将哈希用于如下目的:

  areas = {
    'Area1' => [12345], 
    'Area2' => [54321]
  }

但是按照您的结构,您可以执行以下操作:

  areas = [['Area1', 12345], ['Area2', 54321]]
  new_area = ['Area1', 33441]

  areas.map { |area|
    if area.first == new_area.first
      area + [new_area.last]
    else
      area
    end
  end

还只是为了让您将来更容易找到它,这与 Rails(位于 Ruby 之上的 Web 框架)无关,而是与 Ruby 本身(编程语言)有关。

【讨论】:

    【解决方案3】:

    如果您想坚持使用数组,请尝试以下操作:

    arr1 = [['Area1', 12345], ['Area2', 54321]]
    arr2 = ['Area1', 33441]
    arr1.inject([]){|r,i| r.push(i[0] == arr2[0] ? i.push(arr2[1]) : i)}
    

    在控制台中:

    2.3.1 :001 > arr1 = [['Area1', 12345], ['Area2', 54321]]
     => [["Area1", 12345], ["Area2", 54321]] 
    2.3.1 :002 > arr2 = ['Area1', 33441]
     => ["Area1", 33441] 
    2.3.1 :003 > arr1.inject([]){|r,i| r.push(i[0] == arr2[0] ? i.push(arr2[1]) : i)}
     => [["Area1", 12345, 33441], ["Area2", 54321]] 
    

    看看 polmiro 的回答,我更喜欢他的 map 而不是我的 inject。我相信它们本质上是相同的。除了map 为您创建一个新的array,而使用inject 您必须传入新的array。而且,map 正在执行隐含的 push,而对于 inject,您必须自己完成这项工作。干得好,波尔米罗!

    这是他回答的单行版本(如果你喜欢那种东西):

    arr1.map{|a| a[0] == arr2[0] ? a << arr2[1] : a}   
    

    再次在控制台中:

    2.3.1 :001 > arr1 = [['Area1', 12345], ['Area2', 54321]]
     => [["Area1", 12345], ["Area2", 54321]] 
    2.3.1 :002 > arr2 = ['Area1', 33441]
     => ["Area1", 33441] 
    2.3.1 :003 > arr1.map{|a| a[0] == arr2[0] ? a << arr2[1] : a}    
     => [["Area1", 12345, 33441], ["Area2", 54321]]      
    

    【讨论】:

      【解决方案4】:

      对于

      arr = [['Area1', 12345], ['Area2', 54321]]
      to_add = ['Area1', 1212]
      

      我们可以按如下方式计算所需的数组:

      (arr + [to_add]).group_by(&:first).map { |k,v| [k, *v.map(&:last)] }
        #=> [["Area1", 12345, 1212], ["Area2", 54321]]
      

      第一步,使用Enumerable#group_by 产生以下哈希:

      (arr + [to_add]).group_by(&:first)
        #=> {"Area1"=>[["Area1", 12345], ["Area1", 1212]],
        #    "Area2"=>[["Area2", 54321]]} 
      

      为此,我们必须确保group_by 生成一个哈希,其键的顺序与arr.map(&amp;:first).uniq #=&gt; ["Area1", "Area2"] 相同。 (请注意,Array#uniq 通过保留每个元素的第一个实例来保持顺序)。见this SO question。由于Array#each 会按顺序生成arr + [to_add] 的元素,并且对于 Ruby 1.9+ 版本保持哈希键的插入顺序,我们可以确保结果中的元素顺序正确。

      其他人认为将数据表示为哈希可能是更好的数据模型。可能是这样,但这个问题没有提供足够的信息来说明如何使用返回值来确定这一点。因此,我只提供了问题所要求的内容。

      【讨论】:

        猜你喜欢
        • 2022-11-16
        • 2015-07-10
        • 1970-01-01
        • 1970-01-01
        • 2016-12-12
        • 1970-01-01
        • 2013-03-23
        • 1970-01-01
        • 2022-01-22
        相关资源
        最近更新 更多