如何在Ruby中折叠多维哈希数组?

时间:2018-03-13 14:10:08

标签: arrays ruby

背景

嘿所有,我正在尝试使用外部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

1 个答案:

答案 0 :(得分:1)

哦......所以它不是一个包含哈希集合的数组对象。的种类。让我们再试一次:

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

可以缩短(并且更高效):

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

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

所以它是&#34;具有包含散列的数组的数组&#34;

旧答案如下。我把它留在这里是为了记录目的。它基于数据结构是散列数组的假设。

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

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