【问题标题】:Distribute items into containers in twos - Rails将物品分成两部分分配到容器中 - Rails
【发布时间】:2018-02-14 08:22:00
【问题描述】:

我有一个包含 10 个项目的列表——它是一个哈希数组。

[{ id: 1, name: 'one'}, { id: 2, name: 'two' } .. { id: 10, name: 'ten' }]

我还有随机数量的容器——在这种情况下,假设为 3。这些容器是带有数组值的散列。

{ one: [], two: [], three: [] }

我想要做的是遍历容器并一次删除 2 个项目,结果是:

{ 
   one: [{id:1}, {id:2}, {id:7}, {id:8}], 
   two: [{id:3}, {id:4}, {id:9}, {id:10}], 
   three: [{id:5}, {id:6}] 
}

另外,如果项目列表是奇数 (11),最后一个项目仍会被放入下一个容器中。

{ 
   one: [{id:1}, {id:2}, {id:7}, {id:8}], 
   two: [{id:3}, {id:4}, {id:9}, {id:10}], 
   three: [{id:5}, {id:6}, {id:11}] 
}

注意:这里的哈希被剪掉了,所以更容易阅读。

我的解决方案是这样的:(简化)

x = 10
containers = { one: [], two: [], three: [] }

until x < 1 do
    containers.each do |c|
        c << 'x'
        c << 'x'
    end
    x -= 2
end

puts containers

我正在努力思考如何实现这一目标,但我似乎无法让它发挥作用。

【问题讨论】:

  • 你能添加你有哪些格式的项目和你想要的吗?目前您的问题无法理解
  • 这与ruby-on-rails无关。
  • @Amadan - 我在 Rails 上构建它,所以欢迎任何可以使用 Rails 特定功能的解决方案。

标签: ruby-on-rails ruby algorithm


【解决方案1】:

循环对分布到三个 bin 中:

bins = 3
array = 10.times.map { |i| i + 1 }
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

array.
  each_slice(2).                  # divide into pairs
  group_by.                       # group into bins
  with_index { |p, i| i % bins }. # round-robin style
  values.                         # get rid of bin indices
  each(&:flatten!)                # join pairs in each bin

完全不同的方法,按顺序装箱:

base_size, bins_with_extra = (array.size / 2).divmod(bins)
pos = 0
bins.times.map { |i|
  length = 2 * (base_size + (i < bins_with_extra ? 1 : 0)) # how much in this bin?
  array[pos, length].tap { pos += length }                 # extract and advance
}
# => [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]

如果您绝对需要将其放入哈希中,

Hash[%i(one two three).zip(binned_array)]
# => {:one=>[1, 2, 7, 8], :two=>[3, 4, 9, 10], :three=>[5, 6]}

Stefan Pochmann 暗示的可爱(但可能不如性能)解决方案:

bins.times.with_object(array.to_enum).map { |i, e|
  Array.new(2 * (base_size + (i < bins_with_extra ? 1 : 0))) { e.next }
}

【讨论】:

  • 你也可以group_by.with_index 避免索引,所以你可以只each(&amp;:flatten!) 值。
  • 感谢@Stefan,这确实更简单;我编辑了它。#2 看起来也比它需要的更复杂,但无法弄清楚:P
  • array[pos ... pos += length]?
  • @StefanPochmann 不错 :) 但tap 并不是让我烦恼的事情;我讨厌必须在map 之外初始化pos,而且我不喜欢计算长度:P 哦,好吧...
  • 好的,那么你也不会喜欢e = array.to_enumArray.new(length) { e.next } :-)
【解决方案2】:

这只是为了展示一种不同的方法(我自己可能不会使用这个)。

给定一个项目数组和容器哈希:

items = (1..10).to_a
containers = { one: [], two: [], three: [] }

您可以dup 数组(为了不修改原始数组)并在哈希中构建cycles each_value 的枚举器:

array = items.dup
enum = containers.each_value.cycle

使用上述方法,您可以将数组中的shift 2 项和push 它们放到next 容器中until 数组为emtpy?

enum.next.push(*array.shift(2)) until array.empty?

结果:

containers
#=> {:one=>[1, 2, 7, 8], :two=>[3, 4, 9, 10], :three=>[5, 6]}

【讨论】:

  • 很多方法! \o/
  • 另一种变体:items.each_slice(2).zip(containers.each_value.cycle) { |s, c| c.push(*s) }
【解决方案3】:

您可以使用 Enumerable#each_slice 在 3 秒内迭代从 010 的范围,然后附加到数组数组:

containers = [
  [],
  [],
  []
]

(1...10).each_slice(3) do |slice|
  containers[0] << slice[0]
  containers[1] << slice[1]
  containers[2] << slice[2]
end

p containers
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

【讨论】:

    猜你喜欢
    • 2020-03-17
    • 1970-01-01
    • 2019-04-10
    • 2020-09-21
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    • 2019-11-05
    • 1970-01-01
    相关资源
    最近更新 更多