ref: 9008ac0b55bc0f507e730e8b3d49fa8103e600e1
parent: 302a6ac701bc2599d4ca98c7d263d364505a3980
author: Antti Järvinen <[email protected]>
date: Mon Dec 7 02:27:37 EST 2015
Rename random to shuffle. Remove count parameteter to simplify its role. Add tests for randomising.
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -496,24 +496,14 @@
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 {
@@ -527,20 +517,16 @@
return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
}
- if countv >= seqv.Len() {
- countv = seqv.Len()
- }
+ shuffled := reflect.MakeSlice(reflect.TypeOf(seq), seqv.Len(), seqv.Len())
- suffled := 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 (
@@ -1501,7 +1487,7 @@
"first": First,
"last": Last,
"after": After,
- "random": Random,
+ "shuffle": Shuffle,
"where": Where,
"delimit": Delimit,
"sort": Sort,
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -25,6 +25,7 @@
"runtime"
"testing"
"time"
+ "math/rand"
"github.com/stretchr/testify/assert"
)
@@ -341,42 +342,70 @@
}
}
-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 {