diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java index a42a664a4..ae35a1682 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java @@ -30,10 +30,14 @@ import gplx.core.brys.Bry_bfr_able; import gplx.core.strings.String_bldr; import gplx.core.strings.String_bldr_; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; +import java.util.List; +import java.util.Map; -// REF.PHP: https://www.php.net/manual/en/language.types.array.php -// also has static functions; REF.PHP: https://www.php.net/manual/en/ref.array.php +// REF.PHP:https://www.php.net/manual/en/language.types.array.php +// also has static functions; REF.PHP:https://www.php.net/manual/en/ref.array.php public class XophpArray implements Bry_bfr_able, Iterable { private final Ordered_hash hash = Ordered_hash_.New(); private int newMemberIdx; @@ -138,6 +142,9 @@ public class XophpArray implements Bry_bfr_able, Iterable { XophpArrayItm itm = (XophpArrayItm)hash.Get_by(key); return itm == null ? null : (T)itm.Val(); } + public XophpArrayItm Get_by_itm(String key) { + return (XophpArrayItm)hash.Get_by(key); + } public void Set(int key, Object val) { this.Set(XophpArrayItm.NewInt(key, val)); } @@ -160,6 +167,9 @@ public class XophpArray implements Bry_bfr_able, Iterable { hash.Del(itm.Key()); } } + public void Del_by(String key) { + hash.Del(key); + } public XophpArrayItm[] To_ary() { return (XophpArrayItm[])hash.To_ary(XophpArrayItm.class); } @@ -309,7 +319,7 @@ public class XophpArray implements Bry_bfr_able, Iterable { return len == 0 ? null : ((XophpArrayItm)array.hash.Get_at(len - 1)).Val(); } - // REF.PHP: https://www.php.net/manual/en/function.array-values.php + // REF.PHP:https://www.php.net/manual/en/function.array-values.php public static XophpArray array_values(XophpArray array) { XophpArray rv = new XophpArray(); int len = array.Len(); @@ -482,7 +492,7 @@ public class XophpArray implements Bry_bfr_able, Iterable { return sb.To_str_and_clear(); } - // REF.PHP: https://www.php.net/manual/en/function.in-array.php + // REF.PHP:https://www.php.net/manual/en/function.in-array.php public static boolean in_array(Object needle, XophpArray haystack) {return in_array(needle, haystack, false);} public static boolean in_array(Object needle, XophpArray haystack, boolean strict) { // if strict, cache needleType @@ -519,7 +529,7 @@ public class XophpArray implements Bry_bfr_able, Iterable { return false; } - // REF.PHP: https://www.php.net/manual/en/function.array-shift.php + // REF.PHP:https://www.php.net/manual/en/function.array-shift.php // Returns the shifted value, or NULL if array is empty or is not an array. public static Object array_shift(XophpArray array) { if (array == null) { @@ -544,7 +554,7 @@ public class XophpArray implements Bry_bfr_able, Iterable { return itms[0].Val(); } - // REF.PHP: https://www.php.net/manual/en/function.array-filter.php + // REF.PHP:https://www.php.net/manual/en/function.array-filter.php public static final int ARRAY_FILTER_USE_BOTH = 1, ARRAY_FILTER_USE_KEY = 2, ARRAY_FILTER_USE_VAL = 0; // XO:USE_VAL is not PHP public static XophpArray array_filter(XophpArray array) {return array_filter(array, NULL_CALLBACK, 0);} public static XophpArray array_filter(XophpArray array, XophpCallback callback) {return array_filter(array, callback, 0);} @@ -578,4 +588,70 @@ public class XophpArray implements Bry_bfr_able, Iterable { return !XophpObject_.empty_obj(arg); } } + // REF.PHP:https://www.php.net/manual/en/function.array-diff.php + public static XophpArray array_diff(XophpArray array1, XophpArray array2, XophpArray... arrays) { + // remove elements + Map> valMap = array_diff__build_val_map(array1); + int array1_len = array1.Len(); + array_diff__remove_common_vals(valMap, array1, array2); + for (XophpArray array : arrays) { + // update map, if array contents changed + if (array1_len != array1.Len()) { + valMap = array_diff__build_val_map(array1); + array1_len = array1.Len(); + } + array_diff__remove_common_vals(valMap, array1, array); + } + return array1; + } + private static Map> array_diff__build_val_map(XophpArray array) { + Map> map = new HashMap<>(); + int len = array.Len(); + for (int i = 0; i < len; i++) { + XophpArrayItm itm = array.Get_at_itm(i); + + // get val as String + String valStr = XophpObject_.ToStr(itm.Val()); + List keyList = map.get(valStr); + if (keyList == null) { + keyList = new ArrayList<>(); + map.put(valStr, keyList); + } + keyList.add(itm.Key()); + } + return map; + } + private static void array_diff__remove_common_vals(Map> lhsValMap, XophpArray lhs, XophpArray rhs) { + int rhsLen = rhs.Len(); + for (int i = 0; i < rhsLen; i++) { + XophpArrayItm rhsItm = rhs.Get_at_itm(i); + String rhsVal = XophpObject_.ToStr(rhsItm.Val()); + List lhsKeyList = lhsValMap.get(rhsVal); + if (lhsKeyList != null) { + for (String lhsKey : lhsKeyList) { + lhs.Del_by(lhsKey); + } + } + } + } + + // REF.PHP:https://www.php.net/manual/en/function.array-diff-key + public static XophpArray array_diff_key(XophpArray array1, XophpArray array2, XophpArray... arrays) { + // remove elements + array_diff_key__remove_common_key(array1, array2); + for (XophpArray array : arrays) { + array_diff_key__remove_common_key(array1, array); + } + return array1; + } + private static void array_diff_key__remove_common_key(XophpArray lhs, XophpArray rhs) { + int rhsLen = rhs.Len(); + for (int i = 0; i < rhsLen; i++) { + XophpArrayItm rhsItm = rhs.Get_at_itm(i); + String rhsKey = rhsItm.Key(); + if (lhs.Has(rhsKey)) { + lhs.Del_by(rhsKey); + } + } + } } diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArrayItm.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArrayItm.java index e92310ccb..47f30e834 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArrayItm.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArrayItm.java @@ -23,8 +23,8 @@ import gplx.String_; import gplx.Type_; import gplx.core.brys.Bry_bfr_able; -public class XophpArrayItm implements Bry_bfr_able { - XophpArrayItm(boolean keyIsInt, int keyAsInt, String key, Object val) { +public class XophpArrayItm implements Bry_bfr_able { + XophpArrayItm(boolean keyIsInt, int keyAsInt, String key, T val) { this.keyIsInt = keyIsInt; this.keyAsInt = keyAsInt; this.key = key; @@ -33,7 +33,7 @@ public class XophpArrayItm implements Bry_bfr_able { public boolean KeyIsInt() {return keyIsInt;} private final boolean keyIsInt; public int KeyAsInt() {return keyAsInt;} private final int keyAsInt; public String Key() {return key;} private final String key; - public Object Val() {return val;} public void Val_(Object v) {this.val = v;} private Object val; + public T Val() {return val;} public void Val_(T v) {this.val = v;} private T val; public void To_bfr(Bry_bfr bfr) { bfr.Add_str_u8(key).Add_byte_eq(); diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpObject_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpObject_.java index 74cbea012..bfc6f2ad7 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpObject_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpObject_.java @@ -96,4 +96,9 @@ public class XophpObject_ { // equivalent to ?? public static Object Coalesce(Object o, Object or) {return isset_obj(o) ? o : or;} + + // equivalent to (string)o; see https://www.php.net/manual/en/function.array-diff.php + public static String ToStr(Object o) { + return o.toString(); + } } diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/libs/services/XomwServiceContainer.java b/400_xowa/src/gplx/xowa/mediawiki/includes/libs/services/XomwServiceContainer.java index 34648e4bd..8e47e4a47 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/libs/services/XomwServiceContainer.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/libs/services/XomwServiceContainer.java @@ -15,15 +15,12 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ package gplx.xowa.mediawiki.includes.libs.services; -import gplx.xowa.mediawiki.XophpArray; import gplx.xowa.mediawiki.XophpArray; import gplx.xowa.mediawiki.XophpCallback; import gplx.xowa.mediawiki.XophpObject_; import gplx.xowa.mediawiki.XophpType_; /* XOTODO: -* array_diff: https://www.php.net/manual/en/function.array-diff.php -* array_diff_key: https://www.php.net/manual/en/function.array-diff-key * XomwAssert: /vendor/wikimedia/Assert/src */ // MW.SRC:1.33.1 diff --git a/400_xowa/tst/gplx/xowa/mediawiki/XophpArrayStaticTest.java b/400_xowa/tst/gplx/xowa/mediawiki/XophpArrayStaticTest.java index 9aefd5195..b1c10317e 100644 --- a/400_xowa/tst/gplx/xowa/mediawiki/XophpArrayStaticTest.java +++ b/400_xowa/tst/gplx/xowa/mediawiki/XophpArrayStaticTest.java @@ -389,6 +389,60 @@ public class XophpArrayStaticTest { XophpArray.reset(array); Gftest.Eq__str("step one", (String)XophpArray.current(array)); } + @Test public void array_diff() { + // test To_str + XophpArray array1 = new XophpArray().Add("a", "0"); + XophpArray arrayInt = new XophpArray().Add("a", 0); + + fxt.Test__eq + ( new XophpArray() + , XophpArray.array_diff(array1, arrayInt) + ); + + // test many + XophpArray array2, array3, array4; + array1 = new XophpArray().Add_many("a", "b", "c", "d"); + array2 = new XophpArray().Add_many("a"); + array3 = new XophpArray().Add_many("c"); + array4 = new XophpArray().Add_many("d"); + + fxt.Test__eq + ( new XophpArray().Add(1, "b") + , XophpArray.array_diff(array1, array2, array3, array4) + ); + + // PHP examples + // Example #1 array_diff() example + array1 = new XophpArray().Add("a", "green").Add_many("red", "blue", "red"); + array2 = new XophpArray().Add("b", "green").Add_many("yellow", "red"); + + fxt.Test__eq + ( new XophpArray().Add(1, "blue") + , XophpArray.array_diff(array1, array2) + ); + } + @Test public void array_diff_key() { + // PHP examples + // Example #1 array_diff_key() example + XophpArray array1, array2; + array1 = new XophpArray<>().Add("blue", 1).Add("red", 2).Add("green", 3).Add("purple", 4); + array2 = new XophpArray<>().Add("green", 5).Add("yellow", 7).Add("cyan", 8); + + fxt.Test__eq + ( new XophpArray().Add("blue", 1).Add("red", 2).Add("purple", 4) + , XophpArray.array_diff_key(array1, array2) + ); + + XophpArray array3; + array1 = new XophpArray<>().Add("blue", 1).Add("red", 2).Add("green", 3).Add("purple", 4); + array2 = new XophpArray<>().Add("green", 5).Add("yellow", 7).Add("cyan", 8); + array3 = new XophpArray<>().Add("blue", 6).Add("yellow", 7).Add("mauve", 8); + + fxt.Test__eq + ( new XophpArray().Add("red", 2).Add("purple", 4) + , XophpArray.array_diff_key(array1, array2, array3) + ); + } } class XophpArray_fxt { public XophpArray Make() {return new XophpArray();}