Ruby:随机化数组中某些元素位置的最有效方法

时间:2014-04-11 10:54:54

标签: ruby

我必须在没有降低性能的情况下解决问题。

我正在整合我的编辑CMS a"赞助"内容系统。赞助内容是有些日子在我的主页上获得随机位置的内容。

我有我的内容课,有些内容有一个标志"赞助"。

在我的HomeController中,我调用了Cover的内容。

@content = Content.for_cover

结果是一个包含20个对象的数组,其中一些对象是赞助的。

内容按默认范围(publication_date DESC)排序,所以在我的主页上,它们是从最新到最旧的订购,但我需要赞助内容"覆盖"默认范围并采取随机位置。

我试图找到最佳解决方案:我不想通过大量查询来降低主页渲染的性能。

有什么想法吗?

更多信息

我添加了一个范围方法,可以过滤赞助商,因此我的查询会返回我发布日期和赞助商在前面订购的所有内容(20)。

 @content = Content.for_cover

给我一​​些回复:

c = [s,s,s,s,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16]

其中s是"赞助"。

我必须把每一个" s"并将其移动到随机位置。

c = [c1,c2,c3,s,c4,c5,c6,c7,c8,s,c9,c10,s,c11,c12,c13,c14,s,c15,c16]

4 个答案:

答案 0 :(得分:3)

array = [ "s1", "s2", "s3", "s4", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12", "c13", "c14", "c15", "c16" ]
array.sort_by.with_index { |el, i| el =~ /s/ ? rand(array.size) : i }
#=> ["c1", "c2", "c3", "c4", "c5", "c6", "c7", "s3", "c8", "c9", "c10", "s4", "s2", "c11", "c12", "c13", "s1", "c14", "c15", "c16"]

当然,在真实应用中,您需要检查el.sponsored?或其他内容而不是el =~ /s/

答案 1 :(得分:1)

编辑以回应评论:

试试这个:

sponsered = @content.delete(&:sponsered)

sponsered.each do |s|
  size = @content.size
  @content.insert(rand(size), s)
end

为什么不使用shuffle

@content = Content.for_cover
@content.shuffle

示例:

array = %w[s s s c c c c c c c c c c c c c c c c]
 => ["s", "s", "s", "c", "c", "c", "c", "c", "c", "c", "c", "c", "c", "c", "c", "c", "c", "c", "c"]
array.shuffle
 => ["c", "s", "c", "c", "s", "c", "c", "c", "c", "c", "s", "c", "c", "c", "c", "c", "c", "c", "c"] 

答案 2 :(得分:0)

c = [ "s", "s", "s", "s", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9",
  "c10", "c11", "c12", "c13", "c14", "c15", "c16" ]
hsh = c.group_by { |e| e[0] }
s = hsh.delete 's'
rest = hsh.map( &:last ).reduce :+
result = ( s + Array.new( rest.size ) ).shuffle.map do |e| e || rest.shift end

答案 3 :(得分:0)

这个(Content类仅用于模型模拟):

class Content
  attr_reader :sponsored

  def initialize(sponsored)
    @sponsored = sponsored
  end

  def inspect
    sponsored.to_s
  end
end

a = Array.new(4) { Content.new(true) } + Array.new(6) { Content.new(false) }
p a
#=> [true, true, true, true, false, false, false, false, false, false]

a.select(&:sponsored).each { |v| a.insert rand(a.size), a.delete(v) }
p a
#=> [true, false, true, false, false, false, true, false, false, true]

如果赞助元素总是在其他元素之前,您可以使用take_while代替select