From 907480c691a915d0692dd91b2a78e0eb4fb17044 Mon Sep 17 00:00:00 2001 From: Josh Rosen Date: Sun, 21 Dec 2025 12:02:59 -0800 Subject: [PATCH] =?UTF-8?q?Fix=20O(n=C2=B2)=20performance=20bug=20in=20uni?= =?UTF-8?q?qArr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous implementation called ArrayBuilder.result() inside the loop, which creates a new array copy on each iteration, resulting in O(n²) behavior. This fix tracks lastAddedKey separately to compare against the previous element's key without rebuilding the array. Also uses a while loop instead of foreach to avoid closure allocation overhead. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- sjsonnet/src/sjsonnet/stdlib/SetModule.scala | 25 +++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/sjsonnet/src/sjsonnet/stdlib/SetModule.scala b/sjsonnet/src/sjsonnet/stdlib/SetModule.scala index 65f4a364..614f1b17 100644 --- a/sjsonnet/src/sjsonnet/stdlib/SetModule.scala +++ b/sjsonnet/src/sjsonnet/stdlib/SetModule.scala @@ -65,22 +65,19 @@ object SetModule extends AbstractFunctionModule { val out = new mutable.ArrayBuilder.ofRef[Lazy] // Set a reasonable size hint - in the worst case (no duplicates), we'll need arrValue.length elements out.sizeHint(arrValue.length) - for (v <- arrValue) { - val outResult = out.result() - if (outResult.length == 0) { + + var lastAddedKey: Val = null + var i = 0 + while (i < arrValue.length) { + val v = arrValue(i) + val vKey = + if (keyF.isInstanceOf[Val.False]) v.force + else keyF.asInstanceOf[Val.Func].apply1(v, pos.noOffset)(ev, TailstrictModeDisabled) + if (lastAddedKey == null || !ev.equal(vKey, lastAddedKey)) { out.+=(v) - } else if (keyF.isInstanceOf[Val.False]) { - if (!ev.equal(outResult.last.force, v.force)) { - out.+=(v) - } - } else if (!keyF.isInstanceOf[Val.False]) { - val keyFFunc = keyF.asInstanceOf[Val.Func] - val o1Key = keyFFunc.apply1(v, pos.noOffset)(ev, TailstrictModeDisabled) - val o2Key = keyFFunc.apply1(outResult.last, pos.noOffset)(ev, TailstrictModeDisabled) - if (!ev.equal(o1Key, o2Key)) { - out.+=(v) - } + lastAddedKey = vKey } + i += 1 } Val.Arr(pos, out.result())