Repository: abs-lang/abs
Pull request #404 (2020-09-21 18:17:59)

array.shuffle()


Patch:
diff --git a/docs/types/array.md b/docs/types/array.md
index 65fb36a..c843a28 100644
--- a/docs/types/array.md
+++ b/docs/types/array.md
@@ -350,6 +350,15 @@ a.shift() # 1
 a # [2, 3]
 ```
 
+### shuffle()
+
+Shuffles elements in the array:
+
+``` py
+a = [1, 2, 3, 4]
+a.shuffle() # [3, 1, 2, 4]
+```
+
 ### some(f)
 
 Returns true when at least one of the elements in the array
diff --git a/evaluator/functions.go b/evaluator/functions.go
index e91d0a3..3dfa66f 100644
--- a/evaluator/functions.go
+++ b/evaluator/functions.go
@@ -9,6 +9,7 @@ import (
 	"io/ioutil"
 	"math"
 	"math/big"
+	mrand "math/rand"
 	"os"
 	"os/exec"
 	"os/user"
@@ -367,6 +368,11 @@ func getFns() map[string]*object.Builtin {
 			Types: []string{object.ARRAY_OBJ, object.STRING_OBJ},
 			Fn:    reverseFn,
 		},
+		// shuffle([1,2,3])
+		"shuffle": &object.Builtin{
+			Types: []string{object.ARRAY_OBJ},
+			Fn:    shuffleFn,
+		},
 		// push([1,2,3], 4)
 		"push": &object.Builtin{
 			Types: []string{object.ARRAY_OBJ},
@@ -1929,13 +1935,16 @@ func reverseFn(tok token.Token, env *object.Environment, args ...object.Object)
 
 	if spec == 0 {
 		// array
-		array := args[0].(*object.Array)
+		elements := args[0].(*object.Array).Elements
+		length := len(elements)
+		newElements := make([]object.Object, length, length)
+		copy(newElements, elements)
 
-		for i, j := 0, len(array.Elements)-1; i < j; i, j = i+1, j-1 {
-			array.Elements[i], array.Elements[j] = array.Elements[j], array.Elements[i]
+		for i, j := 0, len(newElements)-1; i < j; i, j = i+1, j-1 {
+			newElements[i], newElements[j] = newElements[j], newElements[i]
 		}
 
-		return array
+		return &object.Array{Elements: newElements}
 	} else {
 		// string
 		str := []rune(args[0].(*object.String).Value)
@@ -1948,6 +1957,25 @@ func reverseFn(tok token.Token, env *object.Environment, args ...object.Object)
 	}
 }
 
+// shuffle([1,2,3])
+func shuffleFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
+	err := validateArgs(tok, "shuffle", args, 1, [][]string{{object.ARRAY_OBJ}})
+
+	if err != nil {
+		return err
+	}
+
+	array := args[0].(*object.Array)
+	length := len(array.Elements)
+	newElements := make([]object.Object, length, length)
+	copy(newElements, array.Elements)
+
+	mrand.Seed(time.Now().UnixNano())
+	mrand.Shuffle(len(newElements), func(i, j int) { newElements[i], newElements[j] = newElements[j], newElements[i] })
+
+	return &object.Array{Elements: newElements}
+}
+
 // push([1,2,3], 4)
 func pushFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
 	err := validateArgs(tok, "push", args, 2, [][]string{{object.ARRAY_OBJ}, {object.NULL_OBJ,


Test patch:
diff --git a/evaluator/builtin_functions_test.go b/evaluator/builtin_functions_test.go
index fdd2f32..c852a16 100644
--- a/evaluator/builtin_functions_test.go
+++ b/evaluator/builtin_functions_test.go
@@ -385,6 +385,16 @@ func TestReverse(t *testing.T) {
 	testBuiltinFunction(tests, t)
 }
 
+func TestShuffle(t *testing.T) {
+	tests := []Tests{
+		{`(1..1000).shuffle().str() != (1..1000).str();`, true},
+		{`(1..1000).shuffle().len() == (1..1000).len();`, true},
+		{`(1..1000).shuffle().sort().str() == (1..1000).str();`, true},
+	}
+
+	testBuiltinFunction(tests, t)
+}
+
 func TestPush(t *testing.T) {
 	tests := []Tests{
 		{`[1, 2].push("a");`, []interface{}{1, 2, "a"}},