如何使用ActiveRecord对象旋转90°数组

时间:2011-05-09 16:40:09

标签: ruby-on-rails ruby activerecord

我有

@my_objects = [ #<MyObject id: 1, title: "Blah1">,
                #<MyObject id: 2, title: "Blah2">,
                #<MyObject id: 3, title: "Blah3">,
                #<MyObject id: 4, title: "Blah4"> ]

我需要把它变成:

@my_objects = { :id => [ 1, 2, 3, 4],
                :title => [ "Blah1" ... ] }

是否有内置方法或一些标准方法?

我只能想象这个

@my_objects.inject({}){ |h, c| c.attributes.each{ |k,v| h[k] ||= []; h[k] << v }; h }

这个问题是在我考虑this particular question

的时候诞生的

3 个答案:

答案 0 :(得分:5)

首先,使用 Enumerable#map (类似@o.map { |e| [e.id, e.title] })将ActiveRecord数组转换为简化的纯Ruby对象,如下所示:

a = [[1, "Blah1"], [2, "Blah2"], [3, "Blah3"], [4, "Blah4"]]

然后:

a.transpose.zip([:id, :title]).inject({}) { |m, (v,k)| m[k] = v; m }

替代解决方案: 如果你只是做了一些平淡无奇的事情,它可能不那么棘手且更容易阅读:

i, t = a.transpose
{ :id => i, :title => t }

你得到的任何一种方式:

 => {:title=>["Blah1", "Blah2", "Blah3", "Blah4"], :id=>[1, 2, 3, 4]} 

更新: Tokland有一个值得引用的改进:

Hash[[:id, :title].zip(a.transpose)]

答案 1 :(得分:2)

你在那里是正确的轨道,这种枢轴没有自定义方法,它应该可以工作,但请记住ActiveRecord属性键是字符串:

@my_objects.inject({ }) { |h, c| c.attributes.each { |k,v| (h[k.to_sym] ||= [ ]) << v }; h }

如果您不太关心它对新手的超级可读性,您可以使用(x ||= [ ]) << y模式来简化这一点。

答案 2 :(得分:2)

功能方法(不是每个人!):

pairs = @my_objects.map { |obj| obj.attributes.to_a }.flatten(1)   
Hash[pairs.group_by(&:first).map { |k, vs| [k, vs.map(&:second)] }]
#=> {:title=>["Blah1", "Blah2", "Blah3", "Blah4"], :id=>[1, 2, 3, 4]}

像往常一样,Facets允许编写更好的代码;在这种情况下,Enumerable#map_by会避免使用丑陋且错综复杂的模式group_by + map + map

@my_objects.map { |obj| obj.attributes.to_a }.flatten(1).map_by { |k, v| [k, v] }   
#=> {:title=>["Blah1", "Blah2", "Blah3", "Blah4"], :id=>[1, 2, 3, 4]}