Skip to content

Commit

Permalink
Array.flat() and Array.flatMap() #275
Browse files Browse the repository at this point in the history
  • Loading branch information
nilproject committed Jun 10, 2022
1 parent 4aa81ef commit eb04d54
Showing 1 changed file with 118 additions and 71 deletions.
189 changes: 118 additions & 71 deletions NiL.JS/BaseLibrary/Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ public static JSValue find(JSValue self, Arguments args)

var result = undefined;

iterateImpl(self, args, undefined, undefined, false, (value, index, thisBind, jsCallback) =>
iterateImpl(self, args[0], args[1], undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
value = value.CloneImpl(false);

Expand Down Expand Up @@ -420,7 +420,7 @@ public static JSValue findIndex(JSValue self, Arguments args)

var result = -1L;

iterateImpl(self, args, undefined, undefined, false, (value, index, thisBind, jsCallback) =>
iterateImpl(self, args[0], args[1], undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
value = value.CloneImpl(false);

Expand All @@ -436,6 +436,69 @@ public static JSValue findIndex(JSValue self, Arguments args)
return result;
}

private static void flatten(JSValue array, JSValue callbackFn, JSValue thisBind, int depth, Action<JSValue> pushItemCallback)
{
iterateImpl(array, callbackFn, thisBind, undefined, undefined, true, (value, index, thisBind, jsCallback) =>
{
var item = jsCallback?.Call(thisBind, new Arguments { value.CloneImpl(false), index, array }) ?? value;

if (depth > 0 && item.Value is Array)
flatten(item, callbackFn, thisBind, depth - 1, pushItemCallback);
else
pushItemCallback(item);

return true;
});
}

[DoNotEnumerate]
[InstanceMember]
[ArgumentsCount(1)]
public static JSValue flatMap(JSValue self, Arguments args)
{
if (self == null)
self = undefined;
if (self._valueType < JSValueType.Object)
self = self.ToObject();

var targetIndex = 0;
var result = new Array();

flatten(self, args[0], args[1], 1, item =>
{
if (item._valueType >= JSValueType.Undefined)
result[targetIndex] = item.CloneImpl(false);

targetIndex++;
});

return result;
}

[DoNotEnumerate]
[InstanceMember]
[ArgumentsCount(1)]
public static JSValue flat(JSValue self, Arguments args)
{
if (self == null)
self = undefined;
if (self._valueType < JSValueType.Object)
self = self.ToObject();

var targetIndex = 0;
var result = new Array();

flatten(self, null, null, 1, item =>
{
if (item._valueType >= JSValueType.Undefined)
result[targetIndex] = item.CloneImpl(false);

targetIndex++;
});

return result;
}

[DoNotEnumerate]
[InstanceMember]
[ArgumentsCount(1)]
Expand All @@ -448,7 +511,7 @@ public static JSValue every(JSValue self, Arguments args)

var result = true;

iterateImpl(self, args, undefined, undefined, false, (value, index, thisBind, jsCallback) =>
iterateImpl(self, args[0], args[1], undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
value = value.CloneImpl(false);

Expand All @@ -469,7 +532,7 @@ public static JSValue some(JSValue self, Arguments args)
self = self.ToObject();

var result = true;
iterateImpl(self, args, undefined, undefined, false, (value, index, thisBind, jsCallback) =>
iterateImpl(self, args[0], args[1], undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
value = value.CloneImpl(false);

Expand All @@ -491,7 +554,7 @@ public static JSValue filter(JSValue self, Arguments args)

Array result = new Array();

iterateImpl(self, args, undefined, undefined, false, (value, index, thisBind, jsCallback) =>
iterateImpl(self, args[0], args[1], undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
value = value.CloneImpl(false);

Expand All @@ -516,7 +579,7 @@ public static JSValue map(JSValue self, Arguments args)

Array result = new Array();

var len = iterateImpl(self, args, undefined, undefined, false, (value, index, thisBind, jsCallback) =>
var len = iterateImpl(self, args[0], args[1], undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
value = value.CloneImpl(false);

Expand Down Expand Up @@ -574,18 +637,7 @@ public static JSValue from(Arguments args)
}
else
{
var newArgs = new Arguments();
for (var i = 1; i < args.Length; i++)
{
newArgs.Add(args[i]);
}

if (simpleFunction)
{
newArgs.Add(Function.Empty);
}

var len = iterateImpl(arrayLike, newArgs, undefined, undefined, false, callback);
var len = iterateImpl(arrayLike, args[1], args[2], undefined, undefined, false, callback);
result.SetLenght(len);
}

Expand All @@ -602,7 +654,7 @@ public static JSValue forEach(JSValue self, Arguments args)
if (self._valueType < JSValueType.Object)
self = self.ToObject();

iterateImpl(self, args, undefined, undefined, false, (value, index, thisBind, jsCallback) =>
iterateImpl(self, args[0], args[1], undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
value = value.CloneImpl(false);

Expand All @@ -623,7 +675,7 @@ public static JSValue indexOf(JSValue self, Arguments args)
self = undefined;
var result = -1L;

iterateImpl(self, null, args?[1] ?? undefined, undefined, false, (value, index, thisBind, jsCallback) =>
iterateImpl(self, null, null, args?[1] ?? undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
if (Expressions.StrictEqual.Check(args[0], value))
{
Expand All @@ -644,7 +696,7 @@ public static JSValue includes(JSValue self, Arguments args)
{
var result = -1L;

iterateImpl(self, null, args?[1] ?? undefined, undefined, false, (value, index, thisBind, jsCallback) =>
iterateImpl(self, null, null, args?[1] ?? undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
if (args[0].IsNaN() ?
value.IsNaN()
Expand All @@ -661,7 +713,7 @@ public static JSValue includes(JSValue self, Arguments args)
return result != -1L;
}

private static long iterateImpl(JSValue self, Arguments args, JSValue startIndexSrc, JSValue endIndexSrc, bool processMissing, Func<JSValue, long, JSValue, ICallable, bool> callback)
private static long iterateImpl(JSValue self, JSValue callbackFn, JSValue thisBind, JSValue startIndexSrc, JSValue endIndexSrc, bool processMissing, Func<JSValue, long, JSValue, ICallable, bool> callback)
{
Array arraySrc = self._oValue as Array;
bool nativeMode = arraySrc != null;
Expand All @@ -684,16 +736,13 @@ private static long iterateImpl(JSValue self, Arguments args, JSValue startIndex
long startIndex = 0;
long endIndex = 0;
ICallable jsCallback = null;
JSValue thisBind = null;

if (args != null)
if (callbackFn != null)
{
// forEach, map, filter, every, some, reduce
jsCallback = args[0] == null ? null : args[0]._oValue as ICallable;
// forEach, map, filter, every, some, reduce, flatMap
jsCallback = callbackFn == null ? null : callbackFn._oValue as ICallable;
if (jsCallback == null)
ExceptionHelper.Throw(new TypeError("Callback is not a function."));

thisBind = args.Length > 1 ? args[1] : null;
}
else if (startIndexSrc.Exists)
{
Expand Down Expand Up @@ -1302,11 +1351,9 @@ public static JSValue reduce(JSValue self, Arguments args)
{
skip = false;
result = args[1];
args[1] = null;
args._iValue = 1;
}

var len = (skip ? 0 : 1) + iterateImpl(self, args, undefined, undefined, false, (value, index, thisBind, jsCallback) =>
var len = (skip ? 0 : 1) + iterateImpl(self, args[0], null, undefined, undefined, false, (value, index, thisBind, jsCallback) =>
{
value = value.CloneImpl(false);

Expand Down Expand Up @@ -1561,7 +1608,7 @@ public static JSValue slice(JSValue self, Arguments args)

var result = new Array();
var index = 0L;
iterateImpl(self, null, args[0], args[1], true, (value, itemIndex, thisBind, jsCallback) =>
iterateImpl(self, null, null, args[0], args[1], true, (value, itemIndex, thisBind, jsCallback) =>
{
if (value.Exists)
{
Expand Down Expand Up @@ -2181,55 +2228,55 @@ internal protected override JSValue GetProperty(JSValue key, bool forWrite, Prop
switch (key._valueType)
{
case JSValueType.Integer:
{
isIndex = (key._iValue & int.MinValue) == 0;
index = key._iValue;
break;
}
{
isIndex = (key._iValue & int.MinValue) == 0;
index = key._iValue;
break;
}
case JSValueType.Double:
{
isIndex = key._dValue >= 0 && key._dValue < uint.MaxValue && (long)key._dValue == key._dValue;
if (isIndex)
index = (int)(uint)key._dValue;
break;
}
{
isIndex = key._dValue >= 0 && key._dValue < uint.MaxValue && (long)key._dValue == key._dValue;
if (isIndex)
index = (int)(uint)key._dValue;
break;
}
case JSValueType.String:
{
if (string.CompareOrdinal("length", key._oValue.ToString()) == 0)
return length;

var skey = key._oValue.ToString();
if (skey.Length > 0 && '0' <= skey[0] && '9' >= skey[0])
{
int si = 0;
if (Tools.ParseJsNumber(skey, ref si, out double dindex)
&& (si == skey.Length)
&& dindex >= 0
&& dindex < uint.MaxValue
&& (long)dindex == dindex)
if (string.CompareOrdinal("length", key._oValue.ToString()) == 0)
return length;

var skey = key._oValue.ToString();
if (skey.Length > 0 && '0' <= skey[0] && '9' >= skey[0])
{
isIndex = true;
index = (int)(uint)dindex;
int si = 0;
if (Tools.ParseJsNumber(skey, ref si, out double dindex)
&& (si == skey.Length)
&& dindex >= 0
&& dindex < uint.MaxValue
&& (long)dindex == dindex)
{
isIndex = true;
index = (int)(uint)dindex;
}
}
}

break;
}
break;
}
default:
{
if (key._valueType >= JSValueType.Object)
{
key = key.ToPrimitiveValue_String_Value();
var keyValue = key.Value;
if (keyValue != null && string.CompareOrdinal("length", keyValue.ToString()) == 0)
return length;
if (key._valueType >= JSValueType.Object)
{
key = key.ToPrimitiveValue_String_Value();
var keyValue = key.Value;
if (keyValue != null && string.CompareOrdinal("length", keyValue.ToString()) == 0)
return length;

if (key.ValueType < JSValueType.Object)
repeat = true;
}
if (key.ValueType < JSValueType.Object)
repeat = true;
}

break;
}
break;
}
}
}
while (repeat);
Expand Down

0 comments on commit eb04d54

Please sign in to comment.