【问题标题】:How to collapse a multi-dimensional array of hashes in Ruby?如何在 Ruby 中折叠多维哈希数组?
【发布时间】:2018-03-14 18:22:47
【问题描述】:

背景:

大家好,我正在尝试使用外部 API,并尝试从网站中拉入用户的所有关注者并应用一些排序。

我已经重构了很多代码,但是,有一个部分让我非常艰难。我相信有一种比我所包含的更简单的方法来实现这一点,并且非常感谢任何提示以更雄辩的方式做到这一点。

我的目标很简单。我想将一组哈希数组(我希望这是解释它的正确方法)折叠成一个哈希数组。

问题描述:

我有一个名为 f_collections 的数组,它有 5 个元素。每个元素都是一个大小为 200 的数组。这些数组的每个子元素都是大约 10 个键值对的散列。我对此的最佳表示如下:

f_collections = [ collection1, collection2, ..., collection5 ]
collection1 = [ hash1, hash2, ..., hash200]
hash1 = { user_id: 1, user_name: "bob", ...}

我正在尝试将这个多维数组折叠成一个哈希数组。由于有五个集合数组,这意味着结果数组将有 1000 个元素 - 所有这些元素都是散列。

followers = [hash1, hash2, ..., hash1000]

代码(即我不想保留的尝试):

我已经让它与一个 非常丑陋 的代码(见下文)一起工作,带有嵌套的 if 语句、块、for 循环等......这东西读起来简直就是一场噩梦我已经尽我最大的努力研究以更简单的方式做到这一点的方法,我就是不知道怎么做。我尝试过扁平化,但它似乎不起作用。

我主要只是包含这段代码来表明我已经非常努力地解决了这个问题,虽然我解决了它,但一定有更好的方法!

注意:我在下面的代码中将一些变量简化为整数,以使其更具可读性。

for n in 1..5 do
        if n < 5
          (0..199).each do |j|
            if n == 1
              nj = j
            else
              nj = (n - 1) * 200 + j
            end

            @followers[nj] = @f_collections[n-1].collection[j]
          end
        else
          (0..199).each do |jj|
            njj = (4) * 200  + jj
            @followers[njj] = @f_collections[n-1].collection[jj]
          end
        end

      end

【问题讨论】:

    标签: arrays ruby


    【解决方案1】:

    哦...所以它不是一个包含散列集合的数组对象。有点儿。让我们再试一次:

    flat = f_collection.map do |col|
      col.collection
    end.flatten
    

    可以缩短(并且性能更高)为:

    flat = f_collection.flat_map do |col|
      col.collection
    end
    

    这是因为f_collection 数组中的项目是具有collection 属性的对象,而collection 属性又是一个数组。

    所以它是“具有包含哈希的数组的事物数组”


    旧答案如下。我将其留在这里用于文档目的。它基于数据结构是散列数组的假设。

    只需使用#flatten(或#flatten!,如果您希望这是“内联”)

    flat = f_collections.flatten
    

    例子

    sub1 = [{a: 1}, {a: 2}]
    sub2 = [{a: 3}, {a: 4}]
    collection = [sub1, sub2]
    
    flat = collection.flatten # returns a new collection
    puts flat #> [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
    
    # or use the "inplace"/"destructive" version 
    collection.flatten! # modifies existing collection
    puts collection #> [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
    

    对您现有代码的一些建议:

    不要使用for n in 1..5,使用Ruby-Style枚举:

    ["some", "values"].each do |value|
      puts value
    end
    

    像这样,您不需要对数组的长度 (5) 进行硬编码(没有意识到您删除了指定这些幻数的变量)。如果你想检测最后一次迭代,你可以使用each_with_index:

    a = ["some", "home", "rome"]
    a.each_with_index do |value, index|
      if index == a.length - 1
        puts "Last value is #{value}"
      else
        puts "Values before last: #{value}"
      end
    end
    

    虽然#flatten 可以解决您的问题,但您可能想看看 DIY 解决方案是什么样子的:

    def flatten_recursive(collection, target = [])
      collection.each do |item|
        if item.is_a?(Array)
          flatten_recursive(item, target)
        else
          target << item
        end
      end
      target
    end
    

    或迭代解决方案(仅限于两个级别):

    def flatten_iterative(collection)
      target = []
      collection.each do |sub|
        sub.each do |item|
          target << item
        end
      end
      target
    end
    

    【讨论】:

    • 嘿帕斯卡!感谢您的回答。我只是再次尝试:followers = f_collections.flatten 然后当我检查大小时它仍然显示 5。当我检查 follower[1] 的值时,它仍然是 200 个哈希的集合,而不是单个哈希。在您的示例中,您只显示了一个哈希键/值对,但在每个哈希中大约有 10 个。这会改变什么吗?例如:sub1 = [{a: 1, b: 3, c:6, ...}, {a: 2}] sub2 = [{a: 3}, {a: 4}] 集合 = [sub1, sub2]
    • 另外 - 我删除了变量名以使示例更简单,但我不知道这个 each_with_index 函数!很棒的提示!
    • 你好@gallant 不,哈希中的键数或数组中的元素数没有区别。您能否提供一组简化的数据,以便我可以针对它运行我的代码?你确定你检查了“flatten”返回的新集合吗?
    • 我更新了示例以显示“flatten”和“flatten”之间的区别!
    • 你好@gallant 如果你的数据结构确实是一个“哈希数组”,那么“扁平化”应该可以工作。或许您可以提供一组具有相同结构的简单示例数据?
    猜你喜欢
    • 2020-02-09
    • 2011-07-31
    • 2015-06-13
    • 2012-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-18
    相关资源
    最近更新 更多