我有一系列交易。我需要按名称对交易进行分组,然后选择金额最高的组。超过1个实例。
例如,如果我有一个名为" car"金额为3000美元,与" boat"共计$ 1800,与" house"总计500美元,该方法将选择船,因为它是具有多个交易的最高金额组。
@transactions =
[{"amount"=>-3000, "name"=>"CAR"},
{"amount"=>-600, "name"=>"BOAT"},
{"amount"=>-600, "name"=>"BOAT"},
{"amount"=>-600, "name"=>"BOAT"},
{"amount"=>-125, "name"=>"HOUSE" },
{"amount"=>-125, "name"=>"HOUSE" },
{"amount"=>-125, "name"=>"HOUSE" },
{"amount"=>-125, "name"=>"HOUSE" }]
现在我有这个,但它根据名字的长度选择。
@transactions.group_by {|h| h['name'] }.max_by {|k, v| v.length }.first
如何分组,然后求和,然后在具有多个交易的组中选择最高金额。
答案 0 :(得分:2)
@transactions.group_by { |h| h['name'] }
.map { |k, v| [k, v.inject(0) { |acc, cur| acc + cur['amount'] }] }
.max_by(&:last).first
如果您需要Enumerable
中的单个值,则可能需要reduce
或inject
。
有关详细信息,请参阅Enumerable#inject
上的文档。
答案 1 :(得分:2)
出于好奇:
00:00:00.001 BRT
现在可以在结果上调用$from = "$month/1/$year";
$to = $month+1."/1/$year";
$fromDate = date_create("$from");
$toDate = date_create("$to");
$fromDate->setTime(0,0,0);
$toDate->setTime(0,0,0);
$from = strtotime($fromDate);
$to = strtotime($toDate);
,或使用[*transactions.each_with_object(
Hash.new { |h, k| h[k] = {count: 0, total: 0} }
) do |h, memo|
memo[h['name']].tap do |ct|
ct[:count] += 1
ct[:total] -= h['amount']
end
end.reject { |_, v| v[:count] == 1 }
.sort_by { |_, v| v[:total] }].to_h
#⇒ {
# "BOAT" => {
# :count => 3,
# :total => 1800
# },
# "HOUSE" => {
# :count => 4,
# :total => 500
# }
# }
代替first
来检索一个最大元素。
答案 2 :(得分:2)
这是一种创建哈希数组而不是从给定数组中选择哈希值的方法。
<强>代码强>
def doit(transactions)
name, arr = transactions.each_with_object(Hash.new { |h,k| h[k]=[] }) { |g,h|
h[g["name"]] << g["amount"] }.
reject { |_,v| v.size == 1 }.
min_by { |_,v| v.reduce(:+) }
name ? arr.map { |v| { "amount"=>v, "name"=>name } } : []
end
<强>实施例强>
doit(@transactions)
#=> [{"amount"=>-600, "name"=>"BOAT"},
# {"amount"=>-600, "name"=>"BOAT"},
# {"amount"=>-600, "name"=>"BOAT"}]
doit([{"amount"=>-3000, "name"=>"CAR"}, {"amount"=>-600, "name"=>"BOAT"},
{"amount"=>-125, "name"=>"HOUSE"}])
#=> []
<强>解释强>
The steps for the first example are as follows.
a = @transactions.each_with_object(Hash.new { |h,k| h[k]=[] }) { |g,h|
h[g["name"]] << g["amount"] }
#=> {"CAR"=>[-3000], "BOAT"=>[-600, -600, -600], "HOUSE"=>[-125, -125, -125, -125]}
b = a.reject { |_,v| v.size == 1 }
#=> {"BOAT"=>[-600, -600, -600], "HOUSE"=>[-125, -125, -125, -125]}
name, arr = b.min_by { |_,v| v.reduce(:+) }
#=> ["BOAT", [-600, -600, -600]]
arr
#=> [-600, -600, -600]
name
#=> "BOAT"
arr.map { |v| { "amount"=>v, "name"=>name } }
#=> [{"amount"=>-600, "name"=>"BOAT"},
# {"amount"=>-600, "name"=>"BOAT"},
# {"amount"=>-600, "name"=>"BOAT"}]
表达式
h = Hash.new { |h,k| h[k]=[] }
#=> {}
使用块给定的默认值创建一个空哈希。假设我们写了
h[:dogs] += ["Saffi"]
#=> ["Saffi"]
Ruby首先将其扩展为
h[:dogs] = h[:dogs] + ["Saffi"]
由于h
没有键:dogs
(h
为空),因此在相等的右侧为h[:dogs]
调用默认值,因此表达式变
h[:dogs] = [] + ["Saffi"]
#=> ["Saffi"]
现在
h #=> {:dogs=>["Saffi"]}
表达式
h[:dogs] << "Saffi"
#=> ["Saffi"]
h #=> {:dogs=>["Saffi"]}
类似,因为h[:dogs]
在[]
附加到空数组"Saffi"
之前设置为h[:dogs]
。现在,如果我们写
h[:dogs] << "Nina"
#=> ["Saffi", "Nina"]
h #=> {:dogs=>["Saffi", "Nina"]}
由于h
现在有一个键:dog
,因此不会调用默认块。
另一种写作方式如下:
def doit(transactions)
name, arr = transactions.each_with_object({}) { |g,h|
(h[g["name"]] ||= []) << g["amount"] }.
reject { |_,v| v.size == 1 }.
min_by { |_,v| v.reduce(:+) }
name ? arr.map { |v| { "amount"=>v, "name"=>name } } : []
end
如果h
没有密钥g["name"]
(在这种情况下为h[g["name"]] #=> nil
),则会在h[g["name"]]
之前将[]
设置为g["amount"]
追加。
答案 3 :(得分:1)
第一步。选择“重复”交易:
google.load("elements", "1", {
packages: "keyboard"
});
function onLoad() {
console.log(lang);
var kbd = new google.elements.keyboard.Keyboard(
[google.elements.keyboard.LayoutCode[lang]],
['input1']);
}
google.setOnLoadCallback(onLoad);
第二步。查找具有最大金额的产品名称(在这种情况下,由于负数而最小):
selected = @transactions.group_by { |el| el['name'] }
.select{ |k, v| v.size > 1 }
更新
selected.each_with_object({}) { |(k, v), obj| obj[k] = v.map { |a| a['amount'] }.sum }
.min_by { |k, v| v }.first
答案 4 :(得分:1)
这里有很多好的答案。我想补充一点,你可以通过组合操作来消除大量的迭代。
例如,您可以在group_by
块中执行此操作,而不是在第二步中计算每个组的总和:
sums = Hash.new(0)
groups = transactions.group_by do |t|
sums[t["name"]] += t["amount"]
t["name"]
end
p groups
# => { "CAR" => [ { "amount" => -3000, "name" => "CAR" } ],
# "BOAT" => [ ... ],
# "HOUSE" => [ ... ] }
p sums
# => { "CAR" => -3000, "BOAT" => -1800, "HOUSE" => -500 }
接下来而不是执行groups.select
来消除只有一个成员的群组,然后min_by
来获得最终结果,将前者合并到后者中:
result = groups.min_by do |k,g|
g.size > 1 ? sums[k] : Float::INFINITY
end
p result[1]
# => [ { "amount" => -600, "name" => "BOAT" },
# { "amount" => -600, "name" => "BOAT" },
# { "amount" => -600, "name" => "BOAT" } ]
因为所有小于Float::INFINITY
,所以永远不会选择只有一个成员的组(除非每个组只有一个成员)。
所以......
全部放在一起:
sums = Hash.new(0)
result =
transactions.group_by {|t|
sums[t["name"]] += t["amount"]
t["name"]
}.min_by {|k,g| g.size > 1 ? sums[k] : Float::INFINITY }[1]
p result
# => [ { "amount" => -600, "name" => "BOAT" },
# { "amount" => -600, "name" => "BOAT" },
# { "amount" => -600, "name" => "BOAT" } ]
您还可以将所有这些组合成一个reduce
并仅对数据进行一次迭代,但它不是非常Rubyish:
sums = Hash.new(0)
groups = Hash.new {|h,k| h[k] = [] }
min_sum = Float::INFINITY
result = transactions.reduce do |min_group, t|
name = t["name"]
sum = sums[name] += t["amount"]
(group = groups[name]) << t
if group.size > 1 && sum < min_sum
min_sum, min_group = sum, group
end
min_group
end
请注意,您可以将变量声明之外的所有内容移动到传递给reduce
(而不是nil
)的数组中,但这会影响可读性。