User Rating 0.0
Total Usage 0 times
Input (Mutator Code)
Examples:
Output (Immutable Code)
Is this tool helpful?

Your feedback helps us improve.

About

JavaScript array mutator methods (push, pop, splice, sort, reverse, shift, unshift) modify the original array in place. This is the single largest source of state-mutation bugs in front-end applications. A stale reference to a mutated array propagates through React state, Redux reducers, or any shared-memory context and produces rendering artifacts that are extremely difficult to trace. This tool rewrites mutator calls into their immutable accessor equivalents using spread syntax, slice, concat, and filter. The output preserves the original semantics while guaranteeing the source array remains untouched. It handles assignment contexts, standalone calls, chained expressions, and the return-value difference between mutators that return lengths versus mutators that return elements.

Limitations: this converter operates on surface-level pattern matching, not a full AST. Deeply nested or dynamically computed method names (e.g., arr[method]()) will not be detected. Minified or obfuscated code may produce incorrect results. For production refactoring of large codebases, pair this with ESLint rules like no-array-mutation. Pro tip: after conversion, verify that downstream code does not rely on the mutator's return value (e.g., push returns the new length, not the new array).

javascript immutable array array methods mutator to accessor code converter functional programming immutable data

Formulas

Each mutator method follows a deterministic rewrite rule. The converter applies pattern matching against the general form:

identifier.mutator(args)

The transformation rules are:

{
arr.push(a, b) [...arr, a, b]arr.pop() arr.slice(0, −1)arr.shift() arr.slice(1)arr.unshift(a, b) [a, b, ...arr]arr.sort(fn) [...arr].sort(fn)arr.reverse() [...arr].reverse()arr.splice(s, d, ...items) [...arr.slice(0, s), ...items, ...arr.slice(s + d)]

Where arr is any valid JavaScript identifier or member expression, s is the start index, d is the delete count, and items are the replacement elements. The converter preserves whitespace and indentation context. For splice with no delete count argument, the converter defaults d = 0. For assignment contexts (e.g., const result = arr.push(x)), the converter wraps the immutable expression into the assignment target.

Reference Data

Mutator MethodMutates OriginalReturn ValueImmutable EquivalentEquivalent Return ValueNotes
push(...items)YesNew length[...arr, ...items]New arrayReturn value type changes from number to Array
pop()YesRemoved elementarr.slice(0, −1)New array (without last)Original returns the popped element, not the array
shift()YesRemoved elementarr.slice(1)New array (without first)Original returns the shifted element, not the array
unshift(...items)YesNew length[...items, ...arr]New arrayReturn value type changes from number to Array
splice(start, del, ...items)YesArray of removed elements[...arr.slice(0, start), ...items, ...arr.slice(start + del)]New arrayMost complex transformation; original returns removed items
sort(fn)YesSorted array (same ref)[...arr].sort(fn)New sorted arraySpread-then-sort creates a shallow copy first
reverse()YesReversed array (same ref)[...arr].reverse()New reversed arraySpread-then-reverse creates a shallow copy first
fill(val, start, end)YesModified array (same ref)[...arr].fill(val, start, end)New filled arrayES6 mutator, often overlooked
copyWithin(t, s, e)YesModified array (same ref)[...arr].copyWithin(t, s, e)New arrayRarely used but still a mutator
map(fn)NoNew arrayAlready immutableNew arrayAccessor - no conversion needed
filter(fn)NoNew arrayAlready immutableNew arrayAccessor - no conversion needed
slice(s, e)NoNew arrayAlready immutableNew arrayAccessor - no conversion needed
concat(...items)NoNew arrayAlready immutableNew arrayAccessor - no conversion needed
reduce(fn, init)NoAccumulated valueAlready immutableAccumulated valueAccessor - no conversion needed
indexOf(val)NoIndex numberAlready immutableIndex numberAccessor - no conversion needed

Frequently Asked Questions

Yes. The native push and unshift return the new length of the array (a number). The immutable equivalent returns the new array itself. If downstream code depends on the return value being a length (e.g., const len = arr.push(x)), you must manually extract the length with .length after conversion. The converter adds a comment warning when it detects this pattern.
Per the ECMAScript specification, calling splice(start) without a second argument removes all elements from start to the end. The converter detects single-argument splice and produces arr.slice(0, start) as the immutable equivalent, which correctly preserves only elements before start.
Yes. The converter processes chains left-to-right. arr.sort(fn).reverse() becomes [...arr].sort(fn).reverse(). Since the spread copy is created at the first mutator, subsequent chained mutators operate on the already-copied array, so the chain remains safe. Note that the original arr is never touched.
Yes. The regex pattern matches any member expression chain before the dot-method call. So obj.data.items.push(x) converts to [...obj.data.items, x]. However, this only creates a shallow copy of the items array. It does not deep-clone obj or obj.data. For full immutability in nested state trees, you still need to spread or clone each parent level.
The converter operates on raw text patterns and does not parse type annotations. Simple TypeScript that follows standard method call syntax will convert correctly. However, generic type parameters on method calls (e.g., arr.push<T>(x)) or complex type assertions adjacent to the call may confuse the pattern matcher. Strip type annotations first or review the output carefully.
Mutators operate in O(1) for push/pop (amortized) because they modify in place. The immutable spread equivalent is O(n) because it copies the entire array. For arrays with fewer than ~10,000 elements, this difference is negligible (sub-millisecond). For hot loops over large arrays, consider using a persistent data structure library like Immer or Immutable.js instead of naive spread copies.