How can I elegantly get a specific number (>1) of distinct, random elements from a collection?
Consider the following code snippet
sample: anInteger from: aCollection using: aGenerator
| sample |
sample := Set new: anInteger.
[sample size = anInteger]
whileFalse: [ | element |
element := aCollection atRandom: aGenerator.
sample add: element].
^sample asArray
Some remarks
Explicit Generator: It explicitly uses a given generator, i.e., an instance of Random
, which I've called aGenerator
. For mathematical reasons, if you are getting samples for your application, all of them should use the very same generator across your program. Also this will give you an additional advantage: save and later restore the seed
and you will be able to reproduce a previous "random" behavior of your system, which is good for testing.
No check for availability: The code doesn't check that it is possible to get the desired sample, which would be the case if aCollection
doesn't have at least anInteger
different elements.
Classless code: The method should go to some class.
For example:
Random >> sample: anInteger from: aCollection
| sample |
sample := Set new: anInteger.
[sample size = anInteger]
whileFalse: [ | element |
element := aCollection atRandom: self.
sample add: element].
^sample asArray
UPDATE
Here is another approach:
Random >> remove: anInteger from: aCollection
| sample |
sample := OrderedCollection new: anInteger.
anInteger timesRepeat: [| index element |
index := aCollection size atRandom: self.
element := aCollection removeAt: index.
sample add: element].
^sample
Comment
It usually happens that when we want to sample without repetition we also want to remove the elements from the collection as we randomly pick them. In these cases what often happens is that the collection is known to have no repetitions.
This is something that I think looks more or less nice, but is not as efficient as it can be:
yourCollection asSet asOrderedCollection shuffled first: numberOfElements
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With