Array concatenation based on the length
Bug in our code
Recently I encountered a weird bug in the code. When we are aggregating data, we were then combining the results in the single array. And as almost everywhere in the code, we were using the
array.concat(...array2) function to do it. We use this because it is better to return new value, compared to the changed of existing one? I think it is an idea from functional programming?
But then there was a case, when this did not work. Running it locally, I got the following message:
Caught Unhandled Promise Rejection: RangeError: Maximum call stack size exceeded
Concat (original - didn't work)
Checking the Ecma Standard documentation for concat, it seems that if array is spreadable, there is a repetition there, where it is not defined, if it is a for loop or recursion. Recursion could cause this problem.
Let O be ? ToObject(this value). 2. Let A be ? ArraySpeciesCreate(O, 0). 3. Let n be 0. 4. Prepend O to items. 5. For each element E of items, do a. Let spreadable be ? IsConcatSpreadable(E). b. If spreadable is true, then i. Let k be 0. ii. Let len be ? LengthOfArrayLike(E). iii. If n + len > 253 - 1, throw a TypeError exception. # this part is most likely the problematic part iv. Repeat, while k < len, 1. Let P be ! ToString(𝔽(k)). 2. Let exists be ? HasProperty(E, P). 3. If exists is true, then a. Let subElement be ? Get(E, P). b. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), subElement). 4. Set n to n + 1. 5. Set k to k + 1. c. Else, i. NOTE: E is added as a single item rather than spread. ii. If n ≥ 253 - 1, throw a TypeError exception. iii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), E). iv. Set n to n + 1. 6. Perform ? Set(A, "length", 𝔽(n), true). 7. Return A.
Based on the testing this case (a bit over 150.000 array list and empty list), this only happens if the longer array is added the the empty array -> which in our case happens, since this the the first result added to the final results. If the order is reversed, so empty array is added to the longer one, the error does not appear.
But reversing the order in this case would not work, since I would still be adding the too long one in the next step of the for loop. So I tested some of the other syntax.
Push (didn't work)
array1.push(...array2) syntax has the same error. Even though the Ecma Standard documentation for push does not seems to have the same recursion. It only has the for loop. Maybe it is a NodeJS implementation then.
Let O be ? ToObject(this value). 2. Let len be ? LengthOfArrayLike(O). 3. Let argCount be the number of elements in items. 4. If len + argCount > 253 - 1, throw a TypeError exception. 5. For each element E of items, do a. Perform ? Set(O, ! ToString(𝔽(len)), E, true). b. Set len to len + 1. 6. Perform ? Set(O, "length", 𝔽(len), true). 7. Return 𝔽(len).
On the other hand, the spread syntax
[...array1, array2] did work. At least this place now no longer have any problems anymore. Not sure what this one worked, though...