Let's say I have an array of product IDs and Quantities, like this:
假设我有一个产品id和数量的数组,像这样:
records = [[1, 10], [1, 30], [4, 10], [4, 100], [5, 45]]
What's the easiest/most efficient way in Ruby to achieve a hash of the combined products and quantities, like this?
在Ruby中,实现组合产品和数量的散列的最简单/最有效的方法是什么?
products_needed = [{id: 1, count:40}, {id: 4, count: 110}, {id:5, count:45}]
4 个解决方案
#1
4
Try this:
试试这个:
records.group_by(&:first).map do |id, records_for_id|
{
id: id,
count: records_for_id.sum(&:last)
}
end
#2
3
If you're in Ruby 2.4+, you can use group_by
followed by transform_values
:
如果您使用的是Ruby 2.4+,可以使用group_by后跟transform_values:
records.group_by(&:first) # => {1=>[[1, 10], [1, 30]], 4=>[[4, 10], [4, 100]], 5=>[[5, 45]]}
records.group_by(&:first).transform_values do |values|
values.sum(&:last)
end # => {1=>40, 4=>110, 5=>45}
#3
1
records
.each_with_object(Hash.new(0)){|(k, v), h| h.merge!(k => v){|_, v1, v2| v1 + v2}}
# => {1=>40, 4=>110, 5=>45}
records
.each_with_object(Hash.new(0)){|(k, v), h| h.merge!(k => v){|_, v1, v2| v1 + v2}}
.map{|k, v| {id: k, count: v}}
# => [{:id=>1, :count=>40}, {:id=>4, :count=>110}, {:id=>5, :count=>45}]
#4
0
You don't have an array of product IDs and Quantities. You have an array of arrays of integers. The easiest way to deal with this array of arrays of integers is to not have an array of arrays of integers but an Order
:
没有产品id和数量的数组。你有一个整数数组数组。处理这个整数数组的最简单的方法是不要有一个整数数组,而要有一个顺序:
class Product
def to_s; 'Some Product' end
alias_method :inspect, :to_s
end
class OrderItem
attr_reader :product, :count
def initialize(product, count)
self.product, self.count = product, count
end
def to_s; "#{count} x #{product}" end
alias_method :inspect, :to_s
private
attr_writer :product, :count
end
class Order
include Enumerable
def initialize(*order_items)
self.order_items = order_items
end
def each(&blk) order_items.each(&blk) end
def items
group_by(&:product).map {|product, order_items| OrderItem.new(product, order_items.sum(&:count)) }
end
def to_s; order_items.map(&:to_s).join(', ') end
alias_method :inspect, :to_s
private
attr_accessor :order_items
end
Now, assuming that you receive your data in the form of an Order
, instead of an array of arrays of integers, like this:
现在,假设您接收的数据是一个顺序的,而不是一个整数数组,如下所示:
product1 = Product.new
product4 = Product.new
product5 = Product.new
order = Order.new(
OrderItem.new(product1, 10),
OrderItem.new(product1, 30),
OrderItem.new(product4, 10),
OrderItem.new(product4, 100),
OrderItem.new(product5, 45)
)
All you need to do is:
你所需要做的就是:
order.items
#=> [40 x Some Product, 110 x Some Product, 45 x Some Product]
The bottom line is: Ruby is an object-oriented language, not an array-of-arrays-of-integers-oriented language, if you use rich objects instead of arrays of arrays of integers, your problem will become much simpler.
底线是:Ruby是一种面向对象的语言,而不是面向整数数组的数组语言,如果您使用富对象而不是整数数组,那么问题就会变得简单得多。
Note: I used order processing as an example. If your problem domain is warehouse management or something else, there will be a similar solution.
注意:我使用订单处理作为例子。如果您的问题域是仓库管理或其他东西,那么将有类似的解决方案。
#1
4
Try this:
试试这个:
records.group_by(&:first).map do |id, records_for_id|
{
id: id,
count: records_for_id.sum(&:last)
}
end
#2
3
If you're in Ruby 2.4+, you can use group_by
followed by transform_values
:
如果您使用的是Ruby 2.4+,可以使用group_by后跟transform_values:
records.group_by(&:first) # => {1=>[[1, 10], [1, 30]], 4=>[[4, 10], [4, 100]], 5=>[[5, 45]]}
records.group_by(&:first).transform_values do |values|
values.sum(&:last)
end # => {1=>40, 4=>110, 5=>45}
#3
1
records
.each_with_object(Hash.new(0)){|(k, v), h| h.merge!(k => v){|_, v1, v2| v1 + v2}}
# => {1=>40, 4=>110, 5=>45}
records
.each_with_object(Hash.new(0)){|(k, v), h| h.merge!(k => v){|_, v1, v2| v1 + v2}}
.map{|k, v| {id: k, count: v}}
# => [{:id=>1, :count=>40}, {:id=>4, :count=>110}, {:id=>5, :count=>45}]
#4
0
You don't have an array of product IDs and Quantities. You have an array of arrays of integers. The easiest way to deal with this array of arrays of integers is to not have an array of arrays of integers but an Order
:
没有产品id和数量的数组。你有一个整数数组数组。处理这个整数数组的最简单的方法是不要有一个整数数组,而要有一个顺序:
class Product
def to_s; 'Some Product' end
alias_method :inspect, :to_s
end
class OrderItem
attr_reader :product, :count
def initialize(product, count)
self.product, self.count = product, count
end
def to_s; "#{count} x #{product}" end
alias_method :inspect, :to_s
private
attr_writer :product, :count
end
class Order
include Enumerable
def initialize(*order_items)
self.order_items = order_items
end
def each(&blk) order_items.each(&blk) end
def items
group_by(&:product).map {|product, order_items| OrderItem.new(product, order_items.sum(&:count)) }
end
def to_s; order_items.map(&:to_s).join(', ') end
alias_method :inspect, :to_s
private
attr_accessor :order_items
end
Now, assuming that you receive your data in the form of an Order
, instead of an array of arrays of integers, like this:
现在,假设您接收的数据是一个顺序的,而不是一个整数数组,如下所示:
product1 = Product.new
product4 = Product.new
product5 = Product.new
order = Order.new(
OrderItem.new(product1, 10),
OrderItem.new(product1, 30),
OrderItem.new(product4, 10),
OrderItem.new(product4, 100),
OrderItem.new(product5, 45)
)
All you need to do is:
你所需要做的就是:
order.items
#=> [40 x Some Product, 110 x Some Product, 45 x Some Product]
The bottom line is: Ruby is an object-oriented language, not an array-of-arrays-of-integers-oriented language, if you use rich objects instead of arrays of arrays of integers, your problem will become much simpler.
底线是:Ruby是一种面向对象的语言,而不是面向整数数组的数组语言,如果您使用富对象而不是整数数组,那么问题就会变得简单得多。
Note: I used order processing as an example. If your problem domain is warehouse management or something else, there will be a similar solution.
注意:我使用订单处理作为例子。如果您的问题域是仓库管理或其他东西,那么将有类似的解决方案。