Skip to content

Commit

Permalink
Merge pull request #9 from ataylor284/master
Browse files Browse the repository at this point in the history
Added rand methods to Iterator.
  • Loading branch information
timyates committed Sep 11, 2013
2 parents 5d5de36 + a011dae commit f9bf162
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 5 deletions.
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,22 @@ And with a `childKey`:

assert map == [dan:[value:'a',kids:[[subnode:[kids:[[item:[value:'a']]]]]]]]

## `rand` functionality for `List`
## `rand` functionality for `List` and `Iterator`

static <T> T rand( List<T> self )
static <T> List<T> rand( List<T> self, int n ) {
static <T> List<T> rand( List<T> self, int n, boolean allowDuplicates ) {
static <T> List<T> rand( List<T> self, int n, boolean allowDuplicates, Random r ) {

Randomly select an element from a list.
static <T> T rand( Iterator<T> self )
static <T> List<T> rand( Iterator<T> self, int n )
static <T> List<T> rand( Iterator<T> self, int n, boolean allowDuplicates )
static <T> List<T> rand( Iterator<T> self, int n, boolean allowDuplicates, Random r )

- The first form returns a single random element from the List.
- The second form returns a `List` of `n` random elements from the List (duplicates allowed)
Randomly select an element from a list or iterator.

- The first form returns a single random element from the List or Iterator.
- The second form returns a `List` of `n` random elements from the List or Iterator (duplicates allowed)
- The third form allows you to specify no duplicates (by passing `false` as the third parameter) and returns a `List`
- The fourth form also returns a `List` and also allows you to set the Random object to be used in the processing. This allows tests to specify a seed so reproducability is assured.

Expand All @@ -194,6 +199,8 @@ If you ask for `0` items, you get an empty list returned.

If you ask for more unique elements than there are items in the list, this throws an `IllegalArgumentException`

When `rand` is called on an Iterator, the entire Iterator is consumed until hasNext() is false and the returned elements are chosen from all the items iterated over with uniform probability.

Example:

def list = [1, 2, 3, 4, 5]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import static org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport.createSimi
/**
* @author Tim Yates
* @author dwoods
* @author Andrew Taylor
*/
class CollectionExtensionMethods {
/**
Expand Down Expand Up @@ -131,6 +132,83 @@ class CollectionExtensionMethods {
ret
}

/**
* Select a single random element from an Iterator
*
* @param self The Iterator to select from
* @return A random element from the List
*/
static <T> T rand( Iterator<T> self ) {
rand( self, 1, true, new Random() )[0]
}

/**
* Select a list of n unique items from the Iterator
*
* @param self The Iterator to select from
* @param n The number of items to select
* @return A List containing the random elements
*/
static <T> List<T> rand( Iterator<T> self, int n ) {
rand( self, n, false, new Random() )
}

/**
* Select a list of n items from the Iterator
*
* @param self The Iterator to select from
* @param n The number of items to select
* @param allowDuplicates If true, the same element can be selected more than once.
* @return A List containing the random elements
*/
static <T> List<T> rand( Iterator<T> self, int n, boolean allowDuplicates ) {
rand( self, n, allowDuplicates, new Random() )
}

/**
* Select a list of n random items from the Iterator
*
* @param self The Iterator to select from
* @param n The number of items to select
* @param allowDuplicates If true, the same element can be selected more than once.
* @param r An instance of Random so we can set a seed to get reproducible random results
* @return A List containing the random elements
*/
static <T> List<T> rand( Iterator<T> self, int n, boolean allowDuplicates, Random rnd ) {
List<T> result

if (allowDuplicates) {

int i = 2
result = [self.next()] * n
while (self.hasNext()) {
T item = self.next()
for (int j = 0; j < n; j++) {
if (rnd.nextInt(i) == 0) {
result[j] = item
}
}
i++
}

} else {

result = self.take(n).toList()
Collections.shuffle(result)
int i = n + 1
while( self.hasNext() ) {
int j = rnd.nextInt(i)
T item = self.next()
if( j < n ) {
result[j] = item
}
i++
}
}

result
}

static class TransposingIterator<T> implements Iterator<T> {
private int idx = 0
private List<Iterator<T>> iterators
Expand Down
27 changes: 26 additions & 1 deletion src/test/groovy/tests/CollectionTests.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,29 @@ class CollectionTests extends Specification {
1..10 as LinkedList | LinkedList
1..10 as Vector | Vector
}
}

def 'check random itertor elements'() {
given:
def range = 1..10
def iter = range.iterator()
expect:
iter.rand() in range
}

def 'check multiple random iterator elements'() {
given:
def list = 0..99
expect:
list.iterator().rand( 3 ).every { it in list }
}

def 'check uniqueness of random iterator elements'() {
given:
def list = 0..99
def size = list.size()
def rand = list.iterator().rand( size, false )
expect:
rand.unique()
}

}

0 comments on commit f9bf162

Please sign in to comment.