Arrays Ruby多维数组-删除第一个位置的重复项,在第二个位置添加数字

Arrays Ruby多维数组-删除第一个位置的重复项,在第二个位置添加数字,arrays,ruby,Arrays,Ruby,对于此阵列: items = [[60, 3], [60, 3], [276, 2], [276, 2], [48, 2], [207, 2], [46, 2], [60, 2], [280, 2], [207, 1], [48, 1], [112, 1], [60, 1], [207, 1], [112, 1], [276, 1], [48, 1], [276, 1], [48, 1], [276, 1], [276, 1], [278, 1], [46, 1], [48, 1], [2

对于此阵列:

items = [[60, 3], [60, 3], [276, 2], [276, 2], [48, 2], [207, 2], [46, 2],
 [60, 2], [280, 2], [207, 1], [48, 1], [112, 1], [60, 1], [207, 1], [112, 1],
 [276, 1], [48, 1], [276, 1], [48, 1], [276, 1], [276, 1], [278, 1], [46, 1],
 [48, 1], [279, 1], [207, 1]]
我想将每个子数组的第一个位置中的常用数字组合起来,然后将第二个位置中的数字相加

例如,您将在这里看到前四个子数组:
[60,3]、[60,3]、[276,2]、[276,2]

这将变成:
[60,6],[276,4]
等等。

试试这个

items.
  group_by {|i| i[0]}.
  map{|key, value| [key,value.inject(0){|sum, x| sum + x[1]}]}
首先,使用创建一个散列,其键是每个数组的第一个元素。所以我们有

{
 60=>[[60, 3], [60, 3], [60, 2], [60, 1]],
 276=>[[276, 2], [276, 2], [276, 1], [276, 1], [276, 1], [276, 1]],
 48=>[[48, 2], [48, 1], [48, 1], [48, 1], [48, 1]],
 207=>[[207, 2], [207, 1], [207, 1], [207, 1]],
 46=>[[46, 2], [46, 1]],
 280=>[[280, 2]],
 112=>[[112, 1], [112, 1]],
 278=>[[278, 1]],
 279=>[[279, 1]]
}
然后,使用方法循环散列来创建所需的结果。要计算每个键的总值,请使用方法对每个数组的所有第二个值求和

[[60, 3], [60, 3], [60, 2], [60, 1]].inject(0) {|sum, x| sum + x[1]} #value is 9

这种数据结构不需要分组、映射和注入

items = [[60, 3], [60, 3], [276, 2], [276, 2], [48, 2], [207, 2], [46, 2],
 [60, 2], [280, 2], [207, 1], [48, 1], [112, 1], [60, 1], [207, 1], [112, 1],
 [276, 1], [48, 1], [276, 1], [48, 1], [276, 1], [276, 1], [278, 1], [46, 1],
 [48, 1], [279, 1], [207, 1]]

# Provide a default value of 0 for all created entries in the hash.
# This saves us from having to conditionally set undefined entries to 0
# http://docs.ruby-lang.org/en/2.0.0/Hash.html
accumulator = Hash.new(0) # Thanks Eric!

# using reduce lets us perform the grouping and adding in one pass.
# as each  item is passed in, we define a member on our accumulator
# for the first number in the pair, and add the value of the second
# number in the pair to our member.
# http://docs.ruby-lang.org/en/2.0.0/Enumerable.html#method-i-reduce
output = items.reduce(accumulator) do |memo, item|
  memo[item[0]] = memo[item[0]] + item[1]
  memo
end

# Finally, convert the hash to an array
output.to_a

# => [[60, 9], [276, 8], [48, 6], [207, 5], [46, 3], [280, 2], [112, 2], [278, 1], [279, 1]]
你可以用

使用任意对象迭代每个元素的给定块 ,并返回最初给定的对象

根据Stefen的评论

传递给块的数组可以这样分解

items.each_with_object(Hash.new(0)) {|(k,v), h| h[k] += v}.to_a

只是为了有另一种选择,为了好玩,和语言一起玩:

items.inject([]) { |arr, el| arr[el[0]] = [el[0], (arr[el[0]] || [_, 0])[1] + el[1]]; arr }.compact
这会更神秘吗

items.inject([]) { |arr, el|
  arr[el[0]] = [el[0], (arr[el[0]] || [_, 0])[1] + el[1]]
  arr
}.compact
#inject
方法以空数组开始,并在遍历
项时添加元素。来自
项目
的每个元素
el
都位于
arr
的索引中。因此,第一个元素
[60,3]
将作为
[60,3]
放置在索引60处(许多孔)

请注意
arr
中每个条目的形式:
[el[0],(arr[el[0]| |[u,0])[1]+el[1]
。这表示第一个元素是公共值,第二个元素加起来初始化为0

此解决方案创建了一个具有许多孔的阵列。
compact
方法删除所有孔

我不能推荐这个解决方案,除非作为语言练习


有趣的是,我打字的时候有几个答案,所以是时候进行(不公平和不科学的)比较了:

require 'benchmark'

items = [[60, 3], [60, 3], [276, 2], [276, 2], [48, 2], [207, 2], [46, 2],
  [60, 2], [280, 2], [207, 1], [48, 1], [112, 1], [60, 1], [207, 1], [112, 1],
  [276, 1], [48, 1], [276, 1], [48, 1], [276, 1], [276, 1], [278, 1], [46, 1],
  [48, 1], [279, 1], [207, 1]]

Benchmark.bmbm do |x|
  x.report(:long) { items.group_by {|i| i[0]}.map{|key, value|     [key,value.inject(0){|sum, x| sum + x[1]}]} }
  x.report(:randym) { items.reduce(Hash.new { |hash, key| hash[key] = 0 }) { |memo, item| memo[item[0]] = memo[item[0]] + item[1]; memo }.to_a }
  x.report(:eric) { items.inject([]) { |arr, el| arr[el[0]] = [el[0], (arr[el[0]] || [0, 0])[1] + el[1]]; arr  }.compact }
end
一些产出:

Rehearsal ------------------------------------------
long     0.000000   0.000000   0.000000 (  0.000016)
randym   0.000000   0.000000   0.000000 (  0.000014)
eric     0.000000   0.000000   0.000000 (  0.000011)
--------------------------------- total: 0.000000sec

             user     system      total        real
long     0.000000   0.000000   0.000000 (  0.000013)
randym   0.000000   0.000000   0.000000 (  0.000011)
eric     0.000000   0.000000   0.000000 (  0.000008)


Rehearsal ------------------------------------------
long     0.000000   0.000000   0.000000 (  0.000024)
randym   0.000000   0.000000   0.000000 (  0.000014)
eric     0.000000   0.000000   0.000000 (  0.000011)
--------------------------------- total: 0.000000sec

             user     system      total        real
long     0.000000   0.000000   0.000000 (  0.000013)
randym   0.000000   0.000000   0.000000 (  0.000011)
eric     0.000000   0.000000   0.000000 (  0.000014)
我的神秘和无法维护可能是最好的,也可能是更糟的。Long和Randym的情况稳定


最后一个注意事项,以防万一:减去每个解决方案返回的数组可以检查我们是否都得到相同的结果:-)

再看一看包含了每个对象的每个_的性能。 看来桑托什是赢家

require 'benchmark'

items = [[60, 3], [60, 3], [276, 2], [276, 2], [48, 2], [207, 2], [46, 2],
  [60, 2], [280, 2], [207, 1], [48, 1], [112, 1], [60, 1], [207, 1], [112, 1],
  [276, 1], [48, 1], [276, 1], [48, 1], [276, 1], [276, 1], [278, 1], [46, 1],
  [48, 1], [279, 1], [207, 1]]

20.times { items.concat items }

Benchmark.bmbm do |x|
  x.report(:long) { items.group_by {|i| i[0]}.map{|key, value|     [key,value.inject(0){|sum, x| sum + x[1]}]} }
  x.report(:randym) { items.reduce(Hash.new { |hash, key| hash[key] = 0 }) { |memo, item| memo[item[0]] = memo[item[0]] + item[1]; memo }.to_a }
  x.report(:eric) { items.inject([]) { |arr, el| arr[el[0]] = [el[0], (arr[el[0]] || [0, 0])[1] + el[1]]; arr  }.compact }
  x.report(:santhosh) { (items.each_with_object(Hash.new(0)) {|a, h| h[a[0]] += a[1]  }).to_a }
end

Rehearsal --------------------------------------------
long       7.130000   0.740000   7.870000 (  8.464277)
randym     6.380000   0.530000   6.910000 (  7.520760)
eric       7.730000   0.680000   8.410000 (  9.135986)
santhosh   5.530000   0.460000   5.990000 (  6.518203)
---------------------------------- total: 29.180000sec

               user     system      total        real
long       7.000000   0.740000   7.740000 (  8.349310)
randym     6.260000   0.540000   6.800000 (  7.426409)
eric       7.630000   0.590000   8.220000 (  8.882282)
santhosh   5.550000   0.460000   6.010000 (  6.736294)
Ruby 2.1.2 Mac Book Pro(2013年) 3 GHz Intel Core i7 8 GB 1600 MHz DDR3

items.group_by(&:shift).map{|k,v| [k,v.flatten.inject(:+)]}.to_h
但在其他人应该理解的实际代码中,我会将其分为两行,并使用这种方法:

result = Hash.new(0)
items.each{|key,value| result[key] += value}


您尝试过的任何特定代码,都可以在@Bijendra上发布。我已经考虑了一段时间了,但是到目前为止还没有任何代码…我假设如果数组是
[[1,1],[1,2],[2,1],[1,3]
你想返回[[1,6],[2,1]],而不是[[1,3],[2,1],[1,3]吗。正确?正确@CarySwoveland。我希望看到你是个天才。这太完美了。非常感谢!这可以通过一些解释性文本来改进,这些文本描述了每个部分的功能。在编写答案时,请记住所有技能水平的读者都会找到答案。
groupby
也可以表示为
groupby(&:first)
,更简洁的
map
可能是
map{key,values |[key,values.map(&:last).inject(:+)}
Super@Long Nguyen您还可以编写
Hash.new(0)
。答案很好,但在我看来,“不需要分组,…”是错误的强调。解决这个问题没有最好的方法,只有不同的方法。你是绝对正确的。请接受我的道歉。你没有什么可道歉的。我希望更大的数据集能让我们更好地理解这里的性能指标。Ruby的大部分性能都与对象创建和垃圾收集有关。上面的内容只是为了好玩,并展示了使用该语言及其标准库可以轻松完成的工作。哇,这很吸引人感谢您记录了所有这些基准@EricPlaton。这真的很有趣。干杯,这是一个有趣的问题,很多人都很喜欢:-)好吧,恕我直言,@Santhosh的解决方案并没有返回预期的数据结构:-发现了,先生!编辑以包括到_a@EricPlaton接得好。我现在已经改正了谢谢。你可以通过
{|(k,v),h | h[k]+=v}
@Stefan,是的,那更好。这是非常好的@Santhosh。非常感谢您的帮助。这个方法很有趣,但是
shift
也会改变
项目
。真的吗?无法使其生成正确的值。你能给我看一下全部代码吗?
result = Hash.new(0)
items.each{|key,value| result[key] += value}
items = [[60, 3], [60, 3], [276, 2], [276, 2], [48, 2], [207, 2], [46, 2],
 [60, 2], [280, 2], [207, 1], [48, 1], [112, 1], [60, 1], [207, 1], [112, 1],
 [276, 1], [48, 1], [276, 1], [48, 1], [276, 1], [276, 1], [278, 1], [46, 1],
 [48, 1], [279, 1], [207, 1]]

hash = Hash.new(0)
items.each do |item|
  hash[item[0]] += item[1]
end

hash
{60=>9, 276=>8, 48=>6, 207=>5, 46=>3, 280=>2, 112=>2, 278=>1, 279=>1}

hash.to_a
[[60, 9], [276, 8], [48, 6], [207, 5], [46, 3], [280, 2], [112, 2], [278, 1], [279, 1]]