# ruby - On average, set true on every fifth item, but not in even order

I have a collection with 100 items.

In an iteration I want to set an object attribute to `true` on 20 % of the items.

But I don't want the setting of true to occur evenly, i.e. on every fifth item.

It's even OK if not exactly 20 % gets `true`, as long as the setting is not set evenly.

You can use rand:

``````def true_20_percent_of_the_time
rand(5) == 0
end
``````

An inefficient but concise way of accomplishing this would be to shuffle your array, slice the first 20 elements, then set the attributes on them.

``````items.shuffle[0..19].each do |item|
item.foo = true
end
``````

If you care about efficiency, you may want to look into modifying the Fisher Yates algorithm.

If you're generating the array:

``````ary = Array.new(2, true) + Array.new(8, false)
ary # => [true, true, false, false, false, false, false, false, false, false]
ary.shuffle # => [false, false, true, false, false, false, false, false, false, true]
``````

That only uses 10 elements as an example, but I'm sure you can figure out how to adjust the count.

The above can be reduced to:

``````ary = (Array.new(2, true) + Array.new(8, false)).shuffle
# => [false, false, true, false, true, false, false, false, false, false]
``````

And that can be reduced to:

``````ary = (([true] * 2) + ([false] * 8)).shuffle
# => [false, false, false, true, false, false, true, false, false, false]
``````

If it's a pre-existing array:

``````ARRAY_SIZE = 10
ary = [false] * ARRAY_SIZE
``````

Then you can slice it and reassign the number of `true` elements needed:

``````ary[0, (ARRAY_SIZE * 0.20).to_i] = [true] * (ARRAY_SIZE * 0.20)
ary # => [true, true, false, false, false, false, false, false, false, false]
``````

And then `shuffle` it:

``````ary.shuffle # => [true, false, false, false, false, false, false, false, false, true]
``````

Let's say you have the array

``````a = Array.new(21) { |i| i }
#=> [0, 1, 2, 3,..., 19, 20]
``````

and you wanted to choose exactly

``````(a.size/5.0).round #=> 4
``````

elements randomly. Then you could write:

``````@lucky_ones = [*(0...a.size)].sample((a.size/5.0).round)
#=> [6, 12, 3, 9]

a.each_with_index do |e,i|
puts "Wow! I've been selected. I'm #{e}" if @lucky_ones.include? i
end

Wow! I've been selected. I'm 3
Wow! I've been selected. I'm 6
Wow! I've been selected. I'm 9
Wow! I've been selected. I'm 12
``````