使用分组将数组拆分为切片

时间:2015-06-22 20:35:23

标签: arrays ruby enumerable

我在这里有一些Ruby代码,但是我确定我没有尽可能高效地完成它。

我有一个对象数组,沿着这一行:

[
    { name: "foo1", location: "new york" },
    { name: "foo2", location: "new york" },
    { name: "foo3", location: "new york" },
    { name: "bar1", location: "new york" },
    { name: "bar2", location: "new york" },
    { name: "bar3", location: "new york" },
    { name: "baz1", location: "chicago" },
    { name: "baz2", location: "chicago" },
    { name: "baz3", location: "chicago" },
    { name: "quux1", location: "chicago" },
    { name: "quux2", location: "chicago" },
    { name: "quux3", location: "chicago" }
]

我想创建一些组 - 比如3 - 其中每组包含半等数量的项目,但穿插在location

我试过这样的事情:

group_size = 3
groups = []

group_size.times do
    groups.push([])
end

i = 0
objects.each do |object|
    groups[i].push(object)
    if i < (group_size - 1)
        i += 1
    else
        i = 0
    end
end

这会返回一个groups对象,如下所示:

[
    [{:name=>"foo1", :location=>"new york"},
     {:name=>"bar1", :location=>"new york"},
     {:name=>"baz1", :location=>"chicago"},
     {:name=>"quux1", :location=>"chicago"}],
    [{:name=>"foo2", :location=>"new york"},
     {:name=>"bar2", :location=>"new york"},
     {:name=>"baz2", :location=>"chicago"},
     {:name=>"quux2", :location=>"chicago"}],
    [{:name=>"foo3", :location=>"new york"},
     {:name=>"bar3", :location=>"new york"},
     {:name=>"baz3", :location=>"chicago"},
     {:name=>"quux3", :location=>"chicago"}]
]

因此,您可以看到每个分组中每个位置都有几个对象。

我玩each_slice()group_by(),甚至尝试使用inject([]) - 但我无法找到更优雅的方法来执行此操作。

我希望这是我忽略的东西 - 我需要考虑更多的location和非偶数的对象。

3 个答案:

答案 0 :(得分:4)

是的,这个与i的簿记通常表明应该有更好的东西。我提出了:

ar =[
    { name: "foo1", location: "new york" },
    { name: "foo2", location: "new york" },
    { name: "foo3", location: "new york" },
    { name: "bar1", location: "new york" },
    { name: "bar2", location: "new york" },
    { name: "bar3", location: "new york" },
    { name: "baz1", location: "chicago" },
    { name: "baz2", location: "chicago" },
    { name: "baz3", location: "chicago" },
    { name: "quux1", location: "chicago" },
    { name: "quux2", location: "chicago" },
    { name: "quux3", location: "chicago" }
]

# next line handles unsorted arrays, irrelevant with this data 
ar = ar.sort_by{|h| h[:location]}

num_groups = 3
groups     = Array.new(num_groups){[]}
wheel      = groups.cycle
ar.each{|h| wheel.next << h}

# done.
p groups
# => [[{:name=>"baz1", :location=>"chicago"}, {:name=>"quux1", :location=>"chicago"}, {:name=>"foo1", :location=>"new york"}, ...]

因为我喜欢cycle方法。

答案 1 :(得分:3)

a.each_slice(group_size).to_a.transpose

如果您的数据在示例中准确描绘,则可以正常工作。如果不是,请提供准确的数据,以便我们更恰当地回答这个问题。

e.g。

a= [
    { name: "foo1", location: "new york" },
    { name: "foo2", location: "new york" },
    { name: "foo3", location: "new york" },
    { name: "bar1", location: "new york" },
    { name: "bar2", location: "new york" },
    { name: "bar3", location: "new york" },
    { name: "baz1", location: "chicago" },
    { name: "baz2", location: "chicago" },
    { name: "baz3", location: "chicago" },
    { name: "quux1", location: "chicago" },
    { name: "quux2", location: "chicago" },
    { name: "quux3", location: "chicago" }
]
group_size = 3
a.each_slice(group_size).to_a.transpose
#=> [
     [
      {:name=>"foo1", :location=>"new york"},
      {:name=>"bar1", :location=>"new york"},
      {:name=>"baz1", :location=>"chicago"},
      {:name=>"quux1", :location=>"chicago"}
    ],
    [
      {:name=>"foo2", :location=>"new york"},
      {:name=>"bar2", :location=>"new york"},
      {:name=>"baz2", :location=>"chicago"},
      {:name=>"quux2", :location=>"chicago"}
    ],
    [
      {:name=>"foo3", :location=>"new york"},
      {:name=>"bar3", :location=>"new york"},
      {:name=>"baz3", :location=>"chicago"},
      {:name=>"quux3", :location=>"chicago"}
    ]
  ]

each_slice 3将在您的示例中将其转换为4个相等的组(编号为1,2,3)。 transpose然后将这4个组变为3组,每组4个。

如果位置不一定按顺序排列,则可以将排序添加到方法链的前面

a.sort_by { |h| h[:location]  }.each_slice(group_size).to_a.transpose

更新

有人指出,transpose的论点参数数量不均匀。我的第一个问题是使用@ CarySwoveland的方法,但是因为他已经发布了它,我想出了一些不同的东西

class Array
  def indifferent_transpose
    arr = self.map(&:dup)
    max = arr.map(&:size).max
    arr.each {|a| a.push(*([nil] * (max - a.size)))}
    arr.transpose.map(&:compact)
  end
end

然后你仍然可以使用相同的方法

a << {name: "foobar1", location: "taiwan" }
a.each_slice(group_size).to_a.indifferent_transpose
#=> [[{:name=>"foo1", :location=>"new york"},
      {:name=>"bar1", :location=>"new york"},
      {:name=>"baz1", :location=>"chicago"},
      {:name=>"quux1", :location=>"chicago"},
      #note the extras values will be placed in the group arrays in order 
      {:name=>"foobar4", :location=>"taiwan"}], 
    [{:name=>"foo2", :location=>"new york"},
      {:name=>"bar2", :location=>"new york"},
      {:name=>"baz2", :location=>"chicago"},
      {:name=>"quux2", :location=>"chicago"}],
    [{:name=>"foo3", :location=>"new york"},
      {:name=>"bar3", :location=>"new york"},
      {:name=>"baz3", :location=>"chicago"},
      {:name=>"quux3", :location=>"chicago"}]]

答案 2 :(得分:2)

这是另一种方法。

<强>代码

def group_em(a, ngroups)
  a.each_with_index.with_object(Array.new(ngroups) {[]}) {|(e,i),arr|
    arr[i%ngroups] << e}
end

示例

a = [
    { name: "foo1",  location: "new york" },
    { name: "foo2",  location: "new york" },
    { name: "foo3",  location: "new york" },
    { name: "bar1",  location: "new york" },
    { name: "bar2",  location: "new york" },
    { name: "bar3",  location: "new york" },
    { name: "baz1",  location: "chicago"  },
    { name: "baz2",  location: "chicago"  },
    { name: "baz3",  location: "chicago"  },
    { name: "quux1", location: "chicago"  },
    { name: "quux2", location: "chicago"  }
]

请注意,我已从问题中省略a的最后一个元素,以使a具有奇数个元素。

group_em(a,3)
  #=> [[{:name=>"foo1",  :location=>"new york"},
  #     {:name=>"bar1",  :location=>"new york"},
  #     {:name=>"baz1",  :location=>"chicago" },
  #     {:name=>"quux1", :location=>"chicago" }],
  #    [{:name=>"foo2",  :location=>"new york"},
  #     {:name=>"bar2",  :location=>"new york"},
  #     {:name=>"baz2",  :location=>"chicago" },
  #     {:name=>"quux2", :location=>"chicago" }],
  #    [{:name=>"foo3",  :location=>"new york"},
  #     {:name=>"bar3",  :location=>"new york"},
  #     {:name=>"baz3",  :location=>"chicago" }]]