Skip to content

Commit

Permalink
Rename random to shuffle. Remove count parameteter to simplify its ro…
Browse files Browse the repository at this point in the history
…le. Add tests for randomising.
  • Loading branch information
EitZei authored and spf13 committed Jan 4, 2016
1 parent 302a6ac commit 9008ac0
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 40 deletions.
30 changes: 8 additions & 22 deletions tpl/template_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,24 +496,14 @@ func After(index interface{}, seq interface{}) (interface{}, error) {
return seqv.Slice(indexv, seqv.Len()).Interface(), nil
}

// Random is exposed to templates, to iterate over N random items in a
// rangeable list.
func Random(count interface{}, seq interface{}) (interface{}, error) {
// Shuffle is exposed to templates, to iterate over items in rangeable list in
// a randomised order.
func Shuffle(seq interface{}) (interface{}, error) {

if count == nil || seq == nil {
if seq == nil {
return nil, errors.New("both count and seq must be provided")
}

countv, err := cast.ToIntE(count)

if err != nil {
return nil, err
}

if countv < 1 {
return nil, errors.New("can't return negative/empty count of items from sequence")
}

seqv := reflect.ValueOf(seq)
seqv, isNil := indirect(seqv)
if isNil {
Expand All @@ -527,20 +517,16 @@ func Random(count interface{}, seq interface{}) (interface{}, error) {
return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
}

if countv >= seqv.Len() {
countv = seqv.Len()
}

suffled := reflect.MakeSlice(reflect.TypeOf(seq), seqv.Len(), seqv.Len())
shuffled := reflect.MakeSlice(reflect.TypeOf(seq), seqv.Len(), seqv.Len())

rand.Seed(time.Now().UTC().UnixNano())
randomIndices := rand.Perm(seqv.Len())

for index, value := range randomIndices {
suffled.Index(value).Set(seqv.Index(index))
shuffled.Index(value).Set(seqv.Index(index))
}

return suffled.Slice(0, countv).Interface(), nil
return shuffled.Interface(), nil
}

var (
Expand Down Expand Up @@ -1501,7 +1487,7 @@ func init() {
"first": First,
"last": Last,
"after": After,
"random": Random,
"shuffle": Shuffle,
"where": Where,
"delimit": Delimit,
"sort": Sort,
Expand Down
65 changes: 47 additions & 18 deletions tpl/template_funcs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"runtime"
"testing"
"time"
"math/rand"

"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -341,43 +342,71 @@ func TestAfter(t *testing.T) {
}
}

func TestRandom(t *testing.T) {
func TestShuffleInputAndOutputFormat(t *testing.T) {
for i, this := range []struct {
count interface{}
sequence interface{}
expect interface{}
success bool
}{
{int(2), []string{"a", "b", "c", "d"}, 2},
{int64(2), []int{100, 200, 300}, 2},
{"1", []int{100, 200, 300}, 1},
{100, []int{100, 200}, 2},
{int32(3), []string{"a", "b"}, 2},
{int64(-1), []int{100, 200, 300}, false},
{"noint", []int{100, 200, 300}, false},
{1, nil, false},
{nil, []int{100}, false},
{1, t, false},
{[]string{"a", "b", "c", "d"}, true},
{[]int{100, 200, 300}, true},
{[]int{100, 200, 300}, true},
{[]int{100, 200}, true},
{[]string{"a", "b"}, true},
{[]int{100, 200, 300}, true},
{[]int{100, 200, 300}, true},
{[]int{100}, true},
{nil, false},
{t, false},
} {
results, err := Random(this.count, this.sequence)
if b, ok := this.expect.(bool); ok && !b {
results, err := Shuffle(this.sequence)
if !this.success {
if err == nil {
t.Errorf("[%d] First didn't return an expected error", i)
}
} else {
resultsv := reflect.ValueOf(results)
sequencev := reflect.ValueOf(this.sequence)

if err != nil {
t.Errorf("[%d] failed: %s", i, err)
continue
}

if resultsv.Len() != this.expect {
t.Errorf("[%d] requested %d random items, got %v but expected %v",
i, this.count, resultsv.Len(), this.expect)
if resultsv.Len() != sequencev.Len() {
t.Errorf("Expected %d items, got %d items", sequencev.Len(), resultsv.Len())
}
}
}
}

func TestShuffleRandomising(t *testing.T) {
// Note that this test can fail with false negative result if the shuffle
// of the sequence happens to be the same as the original sequence. However
// the propability of the event is 10^-158 which is negligible.
sequenceLength := 100
rand.Seed(time.Now().UTC().UnixNano())

for _, this := range []struct {
sequence []int
}{
{rand.Perm(sequenceLength)},
} {
results, _ := Shuffle(this.sequence)

resultsv := reflect.ValueOf(results)

allSame := true
for index, value := range this.sequence {
allSame = allSame && (resultsv.Index(index).Interface() == value)
}

if allSame {
t.Error("Expected sequence to be shuffled but was in the same order")
}
}
}


func TestDictionary(t *testing.T) {
for i, this := range []struct {
v1 []interface{}
Expand Down

0 comments on commit 9008ac0

Please sign in to comment.