2011-04-10 57 views
1

我有一个输出像这样不同数量的阵列的方法:Ruby,数组中的项目?

[["unidentified object", 50], ["person", 22], ["car", 55], ["SUV", 32], ["large_vehicle", 76]] 
[["unidentified object", 167], ["person", 104], ["car", 265], ["SUV", 129], ["large_vehicle", 355]] 
[["unidentified object", 1963], ["person", 413], ["car", 1962], ["SUV", 996], ["large_vehicle", 2027]] 
[["unidentified object", 1526], ["person", 373], ["car", 1560], ["SUV", 765], ["large_vehicle", 1706]] 
[["unidentified object", 1234], ["person", 316], ["car", 1705], ["SUV", 895], ["large_vehicle", 1712]] 

不过,我很想有一个具有每个项目的总数在所有阵列总额,像哈希:

[{:class => "unidentified object", :count => 7234}, {:class => "person", :count => 5231}, {...}] 

这有道理吗?我完全需要了解数组和哈希方法是如何工作的,但是任何提示都会有帮助。

+0

表示总数的数字是错误的。 – sawa 2011-04-10 08:55:31

回答

5

这应该做的伎俩:

data = [ 
    [["unidentified object", 50], ["person", 22], # ... 
    # ... 
] 

pairs = data.flatten(1) 
grouped_by_class = pairs.group_by(&:first) 
output = grouped_by_class.map do |cls, list| 
    {:class => cls, :count => list.map(&:last).inject(&:+)} 
end 

除非你有很好的理由想要的输出完全像你形容的话,我建议去了更简单的东西像{'unidentified object' => 7234, ...},你可以得到作为输出与此代码:

data.flatten(1).inject(Hash.new(0)) do |h, (cls, count)| 
    h[cls] += count 
    h 
end 
+1

你也可以做'data.flatten(1).each_with_object(Hash.new(0)){|(key,value),hash | hash [key] + = value}'。 – 2011-04-10 08:31:29

+0

'#each_with_object'在1.9(IIRC)中也是新的,但我同意它比'#inject'更具可读性。 – Theo 2011-04-10 08:38:37

+0

我唯一反对的是它是唯一的(再次,IIRC)'#each ...'方法,它不返回枚举本身,例如'#each','#each_with_index'等会返回被调用的对象,但是'#each_with_object'违背了这个约定。 – Theo 2011-04-10 08:44:03

1

这项工作?

result = Hash.new 
array.each do |pair| 

    result[pair.first] = 0 unless result.has_key? pair.first 
    result[pair.first]+= pair.last 
end 

不是你想要的输出,而是“类似”的东西。

+2

你可以避免'result [pair.first] = 0,除非result.has_key?如果你说'result = Hash.new(0)',但是当这个参数不是Fixnum时小心使用这种形式的'Hash.new',pair.first'。 – 2011-04-10 08:15:40

+1

针对mu的+1太短的评论。为了详细说明它的含义,当你希望散列的默认值是一个不可变的对象,比如'0'时,你可以在像Hash.new(0)这样的括号中做它。当你想要一个可变对象如'[]'时,你把它放在'Hash.new {[]}'块中,因为你需要每次都生成它。 – sawa 2011-04-10 08:36:32

1

假设你的输出阵列本身在一个可迭代,就可以这样做:

result = {} 

arrays.each do |arr| 
    if result[arr[0]] 
    result[arr[0]] += arr[1] 
    else 
    result[arr[0]] = arr[1] 
    end 
end 

这将遍历的输出阵列,并假设每个子阵列的第一个元素可以被视为一个散列键,如果关键字存在于结果散列中,它会总结每个键的值。如果密钥不存在于散列中,则仅将其添加到结果散列中。

希望这可以做到。

1
a = [[["unidentified object", 50], ["person", 22], ["car", 55], ["SUV", 32], ["large_vehicle", 76]], 
[["unidentified object", 167], ["person", 104], ["car", 265], ["SUV", 129], ["large_vehicle", 355]], 
[["unidentified object", 1963], ["person", 413], ["car", 1962], ["SUV", 996], ["large_vehicle", 2027]], 
[["unidentified object", 1526], ["person", 373], ["car", 1560], ["SUV", 765], ["large_vehicle", 1706]], 
[["unidentified object", 1234], ["person", 316], ["car", 1705], ["SUV", 895], ["large_vehicle", 1712]]] 

在ruby1.9,以下hash会给您一个更自然的形式比你问什么,并且使用这是一个中间形式,给予你想要的形式array

hash = a.flatten(1).each_with_object(Hash.new(0)){|(k, v), h| h[k] += v} 
array = hash.each_with_object([]){|(k, v), h| h.push({class: k, count: v})} 

这里是一个办法做到这一点直接

a.flatten(1).group_by{|k, v| k}.map{|k, v| {class: k, count: v.inject(0){|n, (k, v)| n += v}}} 
4

给你的输入作为数组的数组的数组:

a = [ 
    [["unidentified object", 50], ["person", 22], ["car", 55], ["SUV", 32], ["large_vehicle", 76]], 
    [["unidentified object", 167], ["person", 104], ["car", 265], ["SUV", 129], ["large_vehicle", 355]], 
    [["unidentified object", 1963], ["person", 413], ["car", 1962], ["SUV", 996], ["large_vehicle", 2027]], 
    [["unidentified object", 1526], ["person", 373], ["car", 1560], ["SUV", 765], ["large_vehicle", 1706]], 
    [["unidentified object", 1234], ["person", 316], ["car", 1705], ["SUV", 895], ["large_vehicle", 1712]], 
] 

你可以做一个衬垫:

x = a.flatten(1).inject(Hash.new(0)) { |h, p| h[p[0]] += p[1]; h }.map { |k, v| { :class => k, :count => v } } 
+0

'map'比你好。 – sawa 2011-04-10 08:30:51

1

这是一个简单的增量式解决方案,将在任何合理版本的Ruby中工作...

@r = Hash.new 0 
def merge a 
    a.each { |(c, n)| @r[c] += n } 
end 

merge [["unidentified object", 50], ["person", 22], ["car", 55], ["SUV", 32], ["large_vehicle", 76]] 
merge [["unidentified object", 167], ["person", 104], ["car", 265], ["SUV", 129], ["large_vehicle", 355]] 
merge [["unidentified object", 1963], ["person", 413], ["car", 1962], ["SUV", 996], ["large_vehicle", 2027]] 
merge [["unidentified object", 1526], ["person", 373], ["car", 1560], ["SUV", 765], ["large_vehicle", 1706]] 
merge [["unidentified object", 1234], ["person", 316], ["car", 1705], ["SUV", 895], ["large_vehicle", 1712]] 

p @r.map { |k, v| {:class => k, :count => v}} 
1
data = [ 
    [["unidentified object", 50], ["person", 22], ["car", 55], ["SUV", 32], ["large_vehicle", 76]], 
    [["unidentified object", 167], ["person", 104], ["car", 265], ["SUV", 129], ["large_vehicle", 355]], 
    [["unidentified object", 1963], ["person", 413], ["car", 1962], ["SUV", 996], ["large_vehicle", 2027]], 
    [["unidentified object", 1526], ["person", 373], ["car", 1560], ["SUV", 765], ["large_vehicle", 1706]], 
    [["unidentified object", 1234], ["person", 316], ["car", 1705], ["SUV", 895], ["large_vehicle", 1712]], 
] 

您可以返回很容易这类散,看起来更灵活一点,比哈希数组:

data.flatten(1).inject({}){|h, a| h[a[0]] ||= 0; h[a[0]]+=a[1]; h} 
#=> {"person"=>1228, "unidentified object"=>4940, "SUV"=>2817, "car"=>5547, "large_vehicle"=>5876} 

或者,您的具体目标:

data.flatten(1).inject({}){|h, a| h[a[0]] ||= 0; h[a[0]]+=a[1]; h}.map{|k,v| {:class => k, :count => v}} 
#=> [{:class=>"person", :count=>1228}, {:class=>"unidentified object", :count=>4940}, {:class=>"SUV", :count=>2817}, {:class=>"car", :count=>5547}, {:class=>"large_vehicle", :count=>5876}] 
+0

这与@mu相同,解决方案太短,差别不大。所以@ @ @ @的太短 – fl00r 2011-04-10 11:12:02