0

我有一个json结构:

[["item1", 1, 0], ["item1", 1, 0], ["item1", 1, 0], ["item2", 1, 0], ["item2", 1, 0],["item3", 1, 0],["item4", 1, 0],["item5", 1, 0]..]]

现在我通过循环手动对它们求和以获得以下结果

[["item1", 3, 0], ["item2", 2, 0], ["item4", 1, 0], ["item5", 1, 0]]

还有另一种方法吗?我查看了 Ruby 中的数组模块,但我不确定是否存在另一种方法来做到这一点......

编辑:这就是我正在做的:因为在这里我只总结第二项

    @object = []
    @stat = Hash.new

    @data.each do |d|     

       if @object.include?(d[0])
         @stat [d[0]] += d[2]
       else
         @object << d[0]
         @stat[ d[0] ] = d[2]
       end
   end

编辑2:

我计时了一个班轮解决方案并将其与我自己的进行比较

oneliner:0.005 我的:0.007

这就是我最后使用的:

data.inject(Hash.new(0)) { |stat, item| 
    if item[0] =~ /[a-z][a-z][0-9][0-9][a-z]/ # extra stuff that I needed 
      item[0] = item[0][0..8]
      stat[item[0]] += item[2]
    end
    stat
  }

谢谢大家,我学到了很多。

4

7 回答 7

2

产生数组的单线(如示例中所示):

result = array.inject(Hash.new(0)){|h,item|h[item.first]+=item[2];h}.to_a

如果您希望结果是散列而不是数组,只需省略.to_a

于 2013-09-06T17:11:47.390 回答
1

这应该这样做。

result = array.uniq.each do |a|
  a[1] = array.count { |b| b[0] == a[0] }
end

你也可以使用上面提到的 chunk 方法:

result = []
array.chunk { |k| k }.each { |k, v| result << [k[0], v.length, k[2]] }

这取决于你更喜欢哪个。我更喜欢第一个。

于 2013-09-06T16:46:48.750 回答
1

查看方法。这没有得到你的确切答案,但应该让你到达那里:

a=[["item1", 1, 0], ["item1", 1, 0], ["item1", 1, 0], ["item2", 1, 0], ["item2", 1, 0], ["item3", 1, 0], ["item4", 1, 0], ["item5", 1, 0]]

a.chunk { |k| k }.each { |k, v| p [k[0], v.length] }

["item1", 3]
["item2", 2]
["item3", 1]
["item4", 1]
["item5", 1]
于 2013-09-06T16:47:48.057 回答
1

不确定它是否有效,但它的代码更少

require 'matrix'#not a gem, stdlib
munge = Hash.new(Matrix.zero(1,2))
array = [
  ["item1", 3, 0],
  ["item1", 1, 0],
  ["item1", 1, 0],
  ["item2", 5, 0],
  ["item1", 1, 0],
  ["item4", 1, 0],
  ["item3", 1, 0],
  ["item4", 1, 0]
]
array.each do |(key,a,b)|
  munge[key] += Matrix[[a, b]]
end
# {
#   "item1"=>Matrix[[6, 0]],
#   "item2"=>Matrix[[5, 0]],
#   "item4"=>Matrix[[2, 0]],
#   "item3"=>Matrix[[1, 0]],
# }
#force back into array
munge.each_key { |key| munge[key] = munge[key].row(0).to_a }

稍微编辑它,它可以处理不同数量的“列”

require 'matrix'
array = [
  ["item1", 3, 0, 3, 0],
  ["item1", 1, 0, 1, 0],
  ["item1", 1, 1, 1, 7],
  ["item2", 5, 0, 5, 0],
  ["item1", 1, 0, 1, 2],
  ["item4", 1, 0, 5, 8],
  ["item3", 1, 0, 1, 0],
  ["item4", 1, 0, 1, 0]
]
munge = Hash.new(Matrix.zero(1, array.first.size - 1))
array.each do |(key,*arr)|
  munge[key] += Matrix[arr]
end
#{
#  "item1"=>Matrix[[6, 1, 6, 9]],
#  "item2"=>Matrix[[5, 0, 5, 0]],
#  "item4"=>Matrix[[2, 0, 6, 8]],
#  "item3"=>Matrix[[1, 0, 1, 0]]
#}
于 2013-09-06T17:14:44.003 回答
1

斯宾塞答案的完成,但#chunk确实是这里的正确答案。

如果我正确解释了这个问题,您希望将在位置 0 具有共同值的每个数组的位置 1 和 2 相加。提供的数据具有统一的值,但我的解决方案没有假设这一点。您首先只需要按以下方式分块position[0]

a=[["item1", 1, 0], ["item1", 1, 0], ["item1", 1, 0], ["item2", 1, 0], ["item2", 1, 0], ["item3", 1, 0], ["item4", 1, 0], ["item5", 1, 0]]

chunked = a.chunk { |k| k[0] }
# => [["item1", [["item1", 1, 0], ["item1", 1, 0], ["item1", 1, 0]]], ["item2", [["item2", 1, 0], ["item2", 1, 0]]], ["item3", [["item3", 1, 0]]], ["item4", [["item4", 1, 0]]], ["item5", [["item5", 1, 0]]]]

然后,您可以对每个块中的值求和。这很容易通过获取每个块,将其转置以获取数组中给定索引处的所有值,然后通过注入对有趣的数组求和:

chunked.map {|k, v| t = v.transpose; [k, t[1].inject(:+), t[2].inject(:+)] }
# => [["item1", 3, 0], ["item2", 2, 0], ["item3", 1, 0], ["item4", 1, 0], ["item5", 1, 0]]

结合起来,这很简单:

a.chunk {|k| k[0] }.map do |k, v|
  t = v.transpose
  [k, t[1].inject(:+), t[2].inject(:+)]
end
于 2013-09-06T17:16:44.137 回答
1

它并不漂亮,但如果你想用当前形式的数据来做,你可以使用group_by

data.group_by(&:first).reduce([]) do |acc, (k, v)|
  acc + [[ k, v.map { |x| x[1] }.reduce(:+), v.map { |x| x[2] }.reduce(:+) ]]
end

但是 Ruby 是一种面向对象的语言!当您以这种方式使用它时,它最闪耀:-)。因此,您可能需要考虑为您的数据创建模型类,以便更好地使用它们。如果您愿意,可以使用我共同编写的名为“ id ”的 gem(如 id、ego、superego)轻松地从 Ruby 哈希创建模型:

require 'id'

class Item
  include Id::Model

  field :name
  field :x
  field :y

  def + other
    fail unless name == other.name
    Item.new(name: name, x: x + other.x, y: y + other.y)
  end

end

items = data.map { |(name, x, y)| Item.new(name: name, x: x, y: y) }

然后上面的group_by代码变得更具可读性,并开始更好地表达其意图:

items.group_by(&:name).values.map { |v| v.reduce(:+) }
于 2013-09-06T19:29:19.503 回答
0
a = [["item1", 1, 0], ["item1", 1, 0], ["item1", 1, 0], ["item2", 1, 0], 
     ["item2", 1, 0], ["item3", 1, 0], ["item4", 1, 0], ["item5", 1, 0]]

要获得所需的结果,您可以使用注入

a.inject({}) {|h,e| h[e[0]].nil? ? h[e[0]] = [e[1],e[2]] : h[e[0]] = [h[e[0]][0] + e[1], h[e[0]][2] + e[2]];h }

#=> {"item1"=>[3, 0], "item2"=>[2, 0], "item3"=>[1, 0], "item4"=>[1, 0], "item5"=>[1, 0]}

group_bymapreduce

a.group_by {|i| i[0]}.values.map {|v| v.transpose.map {|x| x[0].class == String ? next : x.reduce(:+)}.map {|x| x.nil? ? v[0][0] : x } }

#=> [["item1", 3, 0], ["item2", 2, 0], ["item3", 1, 0], ["item4", 1, 0], ["item5", 1, 0]]
于 2013-09-07T02:43:44.417 回答