Browse Source

Array.contains (closes #3323) (#9179)

* add Array.contains

* fix flash

* add hl implementation

* test Array.contains through Dynamic but not on JS

* fix python
Dan Korostelev 5 years ago
parent
commit
c4c2d37f80

+ 1 - 1
src/generators/genswf9.ml

@@ -335,7 +335,7 @@ let property ctx fa t =
 		(match p with
 		| "length" -> ident p, Some KInt, false (* UInt in the spec *)
 		| "map" | "filter" when Common.defined ctx.com Define.NoFlashOverride -> ident (p ^ "HX"), None, true
-		| "copy" | "insert" | "remove" | "iterator" | "toString" | "map" | "filter" | "resize" -> ident p , None, true
+		| "copy" | "insert" | "contains" | "remove" | "iterator" | "toString" | "map" | "filter" | "resize" -> ident p , None, true
 		| _ -> as3 p, None, false);
 	| TInst ({ cl_path = ["flash"],"Vector" },_) ->
 		(match p with

+ 4 - 0
src/macro/eval/evalArray.ml

@@ -141,6 +141,10 @@ let remove a equals x =
 		true
 	end
 
+let contains a equals x =
+	let i = indexOf a equals x 0 in
+	i >= 0
+
 let reverse a =
 	a.avalues <- ExtArray.Array.rev (Array.sub a.avalues 0 a.alength)
 

+ 6 - 0
src/macro/eval/evalStdLib.ml

@@ -188,6 +188,11 @@ module StdArray = struct
 		vbool (EvalArray.remove this equals x)
 	)
 
+	let contains = vifun1 (fun vthis x ->
+		let this = this vthis in
+		vbool (EvalArray.contains this equals x)
+	)
+
 	let reverse = vifun0 (fun vthis ->
 		let this = this vthis in
 		EvalArray.reverse this;
@@ -3263,6 +3268,7 @@ let init_standard_library builtins =
 		"push",StdArray.push;
 		"remove",StdArray.remove;
 		"resize",StdArray.resize;
+		"contains",StdArray.contains;
 		"reverse",StdArray.reverse;
 		"shift",StdArray.shift;
 		"slice",StdArray.slice;

+ 9 - 0
std/Array.hx

@@ -223,6 +223,15 @@ extern class Array<T> {
 	**/
 	function remove(x:T):Bool;
 
+
+	/**
+		Returns whether `this` Array contains `x`.
+
+		If `x` is found by checking standard equality, the function returns `true`, otherwise
+		the function returns `false`.
+	**/
+	@:pure function contains( x : T ) : Bool;
+
 	/**
 		Returns position of the first occurrence of `x` in `this` Array, searching front to back.
 

+ 11 - 0
std/cs/_std/Array.hx

@@ -397,6 +397,17 @@ final class Array<T> implements ArrayAccess<T> {
 		return ret;
 	}
 
+	public function contains(x:T):Bool {
+		var __a = __a;
+		var i = -1;
+		var length = length;
+		while (++i < length) {
+			if (__a[i] == x)
+				return true;
+		}
+		return false;
+	}
+
 	public inline function filter(f:T->Bool):Array<T> {
 		var ret = [];
 		for (i in 0...length) {

+ 4 - 0
std/flash/Boot.hx

@@ -297,6 +297,9 @@ class Boot extends flash.display.MovieClip {
 			aproto.insert = function(i, x) {
 				__this__.splice(i, 0, x);
 			};
+			aproto.contains = function(obj) {
+				return __this__.indexOf(obj) != -1;
+			}
 			aproto.remove = function(obj) {
 				var idx = __this__.indexOf(obj);
 				if (idx == -1)
@@ -318,6 +321,7 @@ class Boot extends flash.display.MovieClip {
 			};
 			aproto.setPropertyIsEnumerable("copy", false);
 			aproto.setPropertyIsEnumerable("insert", false);
+			aproto.setPropertyIsEnumerable("contains", false);
 			aproto.setPropertyIsEnumerable("remove", false);
 			aproto.setPropertyIsEnumerable("iterator", false);
 			aproto.setPropertyIsEnumerable("resize", false);

+ 5 - 0
std/hl/types/ArrayBase.hx

@@ -65,6 +65,11 @@ class ArrayBase extends ArrayAccess {
 		throw "Not implemented";
 	}
 
+	public function containsDyn(v:Dynamic):Bool {
+		throw "Not implemented";
+		return false;
+	}
+
 	public function removeDyn(v:Dynamic):Bool {
 		throw "Not implemented";
 		return false;

+ 7 - 0
std/hl/types/ArrayBytes.hx

@@ -210,6 +210,10 @@ class BytesIterator<T> extends ArrayIterator<T> {
 		bytes[pos] = x;
 	}
 
+	public function contains(x:T):Bool {
+		return indexOf(x) != -1;
+	}
+
 	public function remove(x:T):Bool {
 		var idx = indexOf(x);
 		if (idx < 0)
@@ -316,6 +320,9 @@ class BytesIterator<T> extends ArrayIterator<T> {
 	override function insertDyn(pos:Int, v:Dynamic)
 		insert(pos, v);
 
+	override function containsDyn(v:Dynamic)
+		return contains(v);
+
 	override function removeDyn(v:Dynamic)
 		return remove(v);
 

+ 4 - 0
std/hl/types/ArrayDyn.hx

@@ -129,6 +129,10 @@ class ArrayDyn extends ArrayAccess {
 		array.insertDyn(pos, x);
 	}
 
+	public function contains(x:Dynamic):Bool {
+		return array.containsDyn(x);
+	}
+
 	public function remove(x:Dynamic):Bool {
 		return array.removeDyn(x);
 	}

+ 7 - 0
std/hl/types/ArrayObj.hx

@@ -208,6 +208,10 @@ class ArrayObj<T> extends ArrayBase {
 		array[pos] = x;
 	}
 
+	public function contains(x:T):Bool {
+		return indexOf(x) != -1;
+	}
+
 	public function remove(x:T):Bool {
 		var i = indexOf(x);
 		if (i < 0)
@@ -343,6 +347,9 @@ class ArrayObj<T> extends ArrayBase {
 	override function insertDyn(pos:Int, v:Dynamic)
 		insert(pos, v);
 
+	override function containsDyn(v:Dynamic)
+		return contains(v);
+
 	override function removeDyn(v:Dynamic)
 		return remove(v);
 

+ 11 - 0
std/java/_std/Array.hx

@@ -375,6 +375,17 @@ import java.NativeArray;
 		return false;
 	}
 
+	public function contains(x:T):Bool {
+		var __a = __a;
+		var i = -1;
+		var length = length;
+		while (++i < length) {
+			if (__a[i] == x)
+				return true;
+		}
+		return false;
+	}
+		
 	public function indexOf(x:T, ?fromIndex:Int):Int {
 		var len = length, a = __a, i:Int = (fromIndex == null) ? 0 : fromIndex;
 		if (i < 0) {

+ 8 - 0
std/js/_std/Array.hx

@@ -44,6 +44,14 @@ extern class Array<T> {
 		return @:privateAccess HxOverrides.remove(this, x);
 	}
 
+	inline function contains(x:T):Bool {
+		#if (js_es >= 6)
+		return (cast this).includes(x);
+		#else
+		return this.indexOf(x) != -1;
+		#end
+	}
+
 	#if (js_es >= 5)
 	@:pure function indexOf(x:T, ?fromIndex:Int):Int;
 	@:pure function lastIndexOf(x:T, ?fromIndex:Int):Int;

+ 8 - 0
std/lua/_std/Array.hx

@@ -189,6 +189,14 @@ class Array<T> {
 		return false;
 	}
 
+	public function contains(x:T):Bool {
+		for (i in 0...length) {
+			if (this[i] == x)
+				return true;
+		}
+		return false;
+	}
+
 	public function indexOf(x:T, ?fromIndex:Int):Int {
 		var end = length;
 		if (fromIndex == null)

+ 13 - 0
std/neko/_std/Array.hx

@@ -148,6 +148,19 @@
 		return false;
 	}
 
+	public function contains(x:T):Bool {
+		var i = 0;
+		var l = this.length;
+		var a = this.__a;
+		while (i < l) {
+			if (a[i] == x) {
+				return true;
+			}
+			i += 1;
+		}
+		return false;
+	}
+
 	public function indexOf(x:T, ?fromIndex:Int):Int {
 		var len = length;
 		var i:Int = (fromIndex != null) ? fromIndex : 0;

+ 4 - 0
std/php/_std/Array.hx

@@ -54,6 +54,10 @@ final class Array<T> implements ArrayAccess<Int, T> implements IteratorAggregate
 		return wrap(result);
 	}
 
+	public inline function contains(x:T):Bool {
+		return indexOf(x) != -1;
+	}
+
 	public function indexOf(x:T, ?fromIndex:Int):Int {
 		if (fromIndex == null && !Boot.isHxClosure(x) && !Boot.isNumber(x)) {
 			var index = Global.array_search(x, arr, true);

+ 2 - 0
std/python/Boot.hx

@@ -350,6 +350,8 @@ class Boot {
 					createClosure(o, ArrayImpl.indexOf);
 				case "lastIndexOf":
 					createClosure(o, ArrayImpl.lastIndexOf);
+				case "contains":
+					createClosure(o, ArrayImpl.contains);
 				case "remove":
 					createClosure(o, ArrayImpl.remove);
 				case "reverse":

+ 4 - 0
std/python/_std/Array.hx

@@ -80,6 +80,10 @@ extern class Array<T> implements ArrayAccess<T> {
 		return ArrayImpl.remove(this, x);
 	}
 
+	public inline function contains(x:T):Bool {
+		return ArrayImpl.contains(this,x);
+	}
+
 	public inline function reverse():Void {
 		ArrayImpl.reverse(this);
 	}

+ 5 - 0
std/python/internal/ArrayImpl.hx

@@ -108,6 +108,11 @@ class ArrayImpl {
 		}
 	}
 
+	@:ifFeature("dynamic_read.contains", "anon_optional_read.contains", "python.internal.ArrayImpl.contains")
+	public static inline function contains<T>(x:Array<T>,e : T) : Bool {
+		return Syntax.isIn(e, x);
+	}
+
 	@:ifFeature("dynamic_read.shift", "anon_optional_read.shift", "python.internal.ArrayImpl.shift")
 	public static inline function shift<T>(x:Array<T>):Null<T> {
 		if (x.length == 0)

+ 11 - 0
tests/unit/src/unitstd/Array.unit.hx

@@ -200,6 +200,17 @@ a == [i0, i1];
 a.remove(null) == false;
 a == [i0, i1];
 
+// contains
+[].contains(1) == false;
+[1].contains(1) == true;
+[1].contains(2) == false;
+[1,2].contains(1) == true;
+[1,2].contains(2) == true;
+[1,2].contains(3) == false;
+#if !js // see https://github.com/HaxeFoundation/haxe/issues/3330
+([1,2]:Dynamic).contains(2) == true;
+#end
+
 // indexOf
 [].indexOf(10) == -1;
 [10].indexOf(10) == 0;