结合多维数组的第一个元素。

时间:2021-10-04 21:21:46

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.

注意:我使用订单处理作为例子。如果您的问题域是仓库管理或其他东西,那么将有类似的解决方案。