Immutable List in JavaScript
Creating immutable data structures is a cornerstone of functional programming and helps prevent unexpected side effects in your code. This challenge asks you to implement a basic immutable list in JavaScript, ensuring that modifications to the list always return a new list rather than altering the original. This promotes predictability and simplifies debugging.
Problem Description
You are tasked with creating a JavaScript class called ImmutableList. This class should represent a list of values and guarantee immutability. The class should support the following operations:
constructor(initialValues): Takes an array of initial values as input and creates a newImmutableListinstance.push(value): Returns a newImmutableListinstance with the givenvalueadded to the end. The original list should remain unchanged.pop(): Returns a newImmutableListinstance with the last element removed. The original list should remain unchanged. If the list is empty, it should return a new emptyImmutableList.get(index): Returns the value at the givenindexin the list. Should returnundefinedif the index is out of bounds.toArray(): Returns a standard JavaScript array containing the elements of the immutable list.
Key Requirements:
- Immutability: All operations (
push,pop,get) must return a newImmutableListinstance. The originalImmutableListinstance should never be modified directly. - Data Integrity: The list should maintain the order of elements.
- Error Handling:
get()should handle out-of-bounds indices gracefully.pop()should handle empty lists gracefully.
Examples
Example 1:
Input: const list = new ImmutableList([1, 2, 3]);
const newList = list.push(4);
Output: list.toArray() === [1, 2, 3]
newList.toArray() === [1, 2, 3, 4]
Explanation: `push(4)` returns a new list with 4 appended. The original list remains unchanged.
Example 2:
Input: const list = new ImmutableList([1, 2, 3]);
const newList = list.pop();
Output: list.toArray() === [1, 2, 3]
newList.toArray() === [1, 2]
Explanation: `pop()` returns a new list with the last element removed. The original list remains unchanged.
Example 3:
Input: const list = new ImmutableList([]);
const newList = list.pop();
Output: list.toArray() === []
newList.toArray() === []
Explanation: `pop()` on an empty list returns a new empty list.
Example 4:
Input: const list = new ImmutableList([1, 2, 3]);
const value = list.get(1);
Output: value === 2
Explanation: `get(1)` returns the element at index 1.
Example 5:
Input: const list = new ImmutableList([1, 2, 3]);
const value = list.get(5);
Output: value === undefined
Explanation: `get(5)` returns undefined because the index is out of bounds.
Constraints
- The initial values array passed to the constructor can contain any JavaScript data types.
- The
pushandpopoperations should be reasonably efficient (avoiding unnecessary copying if possible, but immutability is the priority). - The
getoperation should have O(1) time complexity. - The
toArrayoperation should have O(n) time complexity, where n is the number of elements in the list.
Notes
- Consider using techniques like spreading (
...) to create new arrays without modifying the original. - Think about how to efficiently manage the underlying data structure to support immutability. A simple array is a good starting point.
- Focus on ensuring that all operations return new instances of
ImmutableList. Testing this thoroughly is crucial. - While performance is a consideration, prioritize correctness and immutability. Optimizations can be explored later.