Repository: abs-lang/abs
Pull request #345 (2020-03-27 00:44:02)

array.diff(other_array)
Computes the difference between 2 arrays
(elements that are only on either of the 2):

```bash
[1, 2, 3].diff([]) # [1, 2, 3]
[1, 2, 3].diff([3]) # [1, 2]
[1, 2, 3].diff([3, 1]) # [2]
[1, 2, 3].diff([1, 2, 3, 4]) # [4]
```


Patch:
diff --git a/docs/types/array.md b/docs/types/array.md
index 3a060e9..e2838b6 100644
--- a/docs/types/array.md
+++ b/docs/types/array.md
@@ -376,6 +376,18 @@ Computes the intersection between 2 arrays:
 [1, 2, 3].intersect([1, 2, 3, 4]) # [1, 2, 3]
 ```
 
+### diff(array)
+
+Computes the difference between 2 arrays
+(elements that are only on either of the 2):
+
+```bash
+[1, 2, 3].diff([]) # [1, 2, 3]
+[1, 2, 3].diff([3]) # [1, 2]
+[1, 2, 3].diff([3, 1]) # [2]
+[1, 2, 3].diff([1, 2, 3, 4]) # [4]
+```
+
 ## Next
 
 That's about it for this section!
diff --git a/evaluator/functions.go b/evaluator/functions.go
index d791d89..6a770d4 100644
--- a/evaluator/functions.go
+++ b/evaluator/functions.go
@@ -203,6 +203,11 @@ func getFns() map[string]*object.Builtin {
 			Types: []string{object.ARRAY_OBJ},
 			Fn:    intersectFn,
 		},
+		// diff(array:[1, 2, 3], array:[1, 2, 3])
+		"diff": &object.Builtin{
+			Types: []string{object.ARRAY_OBJ},
+			Fn:    diffFn,
+		},
 		// map(array:[1, 2, 3], function:f(x) { x + 1 })
 		"map": &object.Builtin{
 			Types: []string{object.ARRAY_OBJ},
@@ -1204,6 +1209,46 @@ func intersectFn(tok token.Token, env *object.Environment, args ...object.Object
 	return &object.Array{Elements: intersection}
 }
 
+// intersect(array:[1, 2, 3], array:[1, 2, 3])
+func diffFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
+	err := validateArgs(tok, "diff", args, 2, [][]string{{object.ARRAY_OBJ}, {object.ARRAY_OBJ}})
+	if err != nil {
+		return err
+	}
+
+	left := args[0].(*object.Array).Elements
+	right := args[1].(*object.Array).Elements
+	foundLeft := map[string]object.Object{}
+	foundRight := map[string]object.Object{}
+	difference := []object.Object{}
+
+	for _, o := range left {
+		foundLeft[string(o.Type())+"__"+o.Inspect()] = o
+	}
+
+	for _, o := range right {
+		foundRight[string(o.Type())+"__"+o.Inspect()] = o
+	}
+
+	for _, o := range left {
+		_, ok := foundRight[string(o.Type())+"__"+o.Inspect()]
+
+		if !ok {
+			difference = append(difference, o)
+		}
+	}
+
+	for _, o := range right {
+		_, ok := foundLeft[string(o.Type())+"__"+o.Inspect()]
+
+		if !ok {
+			difference = append(difference, o)
+		}
+	}
+
+	return &object.Array{Elements: difference}
+}
+
 // map(array:[1, 2, 3], function:f(x) { x + 1 })
 func mapFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
 	err := validateArgs(tok, "map", args, 2, [][]string{{object.ARRAY_OBJ}, {object.FUNCTION_OBJ, object.BUILTIN_OBJ}})


Test patch:
diff --git a/evaluator/builtin_functions_test.go b/evaluator/builtin_functions_test.go
index f48c122..d5dab67 100644
--- a/evaluator/builtin_functions_test.go
+++ b/evaluator/builtin_functions_test.go
@@ -634,6 +634,17 @@ func TestIntersect(t *testing.T) {
 	testBuiltinFunction(tests, t)
 }
 
+func TestDiff(t *testing.T) {
+	tests := []Tests{
+		{`[1,2,3].diff([])`, []int{1, 2, 3}},
+		{`[1,2,3].diff([3])`, []int{1, 2}},
+		{`[1,2,3].diff([3, 1])`, []int{2}},
+		{`[1,2,3].diff([1,2,3,4])`, []int{4}},
+	}
+
+	testBuiltinFunction(tests, t)
+}
+
 func testBuiltinFunction(tests []Tests, t *testing.T) {
 	for _, tt := range tests {
 		evaluated := testEval(tt.input)