Repository: abs-lang/abs
Pull request #344 (2020-03-27 00:35:19)

array.intersect(other_array)
Computes the intersection between 2 arrays:

```bash
[1, 2, 3].intersect([]) # []
[1, 2, 3].intersect([3]) # [3]
[1, 2, 3].intersect([3, 1]) # [1, 3]
[1, 2, 3].intersect([1, 2, 3, 4]) # [1, 2, 3]
```


Patch:
diff --git a/docs/types/array.md b/docs/types/array.md
index c2f47d1..e2838b6 100644
--- a/docs/types/array.md
+++ b/docs/types/array.md
@@ -365,6 +365,29 @@ Returns an array with unique values:
 [1, 1, 1, 2].unique() # [1, 2]
 ```
 
+### intersect(array)
+
+Computes the intersection between 2 arrays:
+
+```bash
+[1, 2, 3].intersect([]) # []
+[1, 2, 3].intersect([3]) # [3]
+[1, 2, 3].intersect([3, 1]) # [1, 3]
+[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 3713bb8..6a770d4 100644
--- a/evaluator/functions.go
+++ b/evaluator/functions.go
@@ -198,6 +198,16 @@ func getFns() map[string]*object.Builtin {
 			Types: []string{object.ARRAY_OBJ},
 			Fn:    sortFn,
 		},
+		// intersect(array:[1, 2, 3], array:[1, 2, 3])
+		"intersect": &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},
@@ -1172,6 +1182,73 @@ func sortFn(tok token.Token, env *object.Environment, args ...object.Object) obj
 	}
 }
 
+// intersect(array:[1, 2, 3], array:[1, 2, 3])
+func intersectFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
+	err := validateArgs(tok, "intersect", 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
+	found := map[string]object.Object{}
+	intersection := []object.Object{}
+
+	for _, o := range right {
+		found[string(o.Type())+"__"+o.Inspect()] = o
+	}
+
+	for _, o := range left {
+		element, ok := found[string(o.Type())+"__"+o.Inspect()]
+
+		if ok {
+			intersection = append(intersection, element)
+		}
+	}
+
+	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 d80bd70..d5dab67 100644
--- a/evaluator/builtin_functions_test.go
+++ b/evaluator/builtin_functions_test.go
@@ -623,6 +623,28 @@ func TestKebab(t *testing.T) {
 	testBuiltinFunction(tests, t)
 }
 
+func TestIntersect(t *testing.T) {
+	tests := []Tests{
+		{`[1,2,3].intersect([])`, []int{}},
+		{`[1,2,3].intersect([3])`, []int{3}},
+		{`[1,2,3].intersect([3, 1])`, []int{1, 3}},
+		{`[1,2,3].intersect([1,2,3,4])`, []int{1, 2, 3}},
+	}
+
+	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)