Skip to the code
cf.

Make a linked list iterable

The starter has a singly linked list. Each node is an object { value, next }. head is the first node and tail the last, both null when the list is empty, and push(value) adds a value at the end.

Make lists iterable: add a [Symbol.iterator]() method that yields the values from head to tail, so a list works with for...of, spread, Array.from, and destructuring. Every value counts, including 0, null, and undefined.

Each loop starts again at the head, so you can loop over a list twice, or inside another loop over the same list.

Add a static method LinkedList.from(iterable) too. It returns a new LinkedList with the values of any iterable, in order: an array, a string, a Set, a generator, or another list. A list made from another list is a separate copy: pushing to one doesn't change the other.

const list = new LinkedList();
list.push(1);
list.push(2);
list.push(3);
[...list]; // [1, 2, 3]
for (const value of list) console.log(value); // logs 1, 2, 3
 
const letters = LinkedList.from("abc");
letters.head.value; // "a"
letters.tail.value; // "c"
[...letters];       // ["a", "b", "c"]

What the tests check

  1. spreads to the values from head to tail
    const list = new LinkedList();
    list.push(1);
    list.push(2);
    list.push(3);
    expect([...list]).toEqual([1, 2, 3]);
    
  2. works with for...of, Array.from, and destructuring
    const list = new LinkedList();
    list.push("a");
    list.push("b");
    list.push("c");
    const seen = [];
    for (const value of list) seen.push(value);
    expect(seen).toEqual(["a", "b", "c"]);
    expect(Array.from(list)).toEqual(["a", "b", "c"]);
    const [first, ...rest] = list;
    expect(first).toBe("a");
    expect(rest).toEqual(["b", "c"]);
    
  3. an empty list yields nothing, a one-item list its one value
    expect([...new LinkedList()]).toEqual([]);
    const list = new LinkedList();
    list.push(42);
    expect([...list]).toEqual([42]);
    
  4. yields falsy values too
    const list = new LinkedList();
    for (const value of [0, "", null, undefined, false]) list.push(value);
    expect([...list]).toEqual([0, "", null, undefined, false]);
    
  5. each loop starts at the head, so nested loops work
    const list = new LinkedList();
    list.push(1);
    list.push(2);
    expect([...list]).toEqual([1, 2]);
    expect([...list]).toEqual([1, 2]);
    const pairs = [];
    for (const a of list) {
      for (const b of list) pairs.push(a * 10 + b);
    }
    expect(pairs).toEqual([11, 12, 21, 22]);
    
  6. the iterator returns { value, done } results
    const list = new LinkedList();
    list.push("x");
    const it = list[Symbol.iterator]();
    expect(it.next()).toEqual({ value: "x", done: false });
    const { value, done } = it.next();
    expect({ value, done }).toEqual({ value: undefined, done: true });
    
  7. LinkedList.from builds a list from any iterable
    const fromSet = LinkedList.from(new Set(["a", "b"]));
    expect(fromSet).toBeInstanceOf(LinkedList);
    expect([...fromSet]).toEqual(["a", "b"]);
    expect(fromSet.head.value).toBe("a");
    expect(fromSet.tail.value).toBe("b");
    function* numbers() {
      yield 1;
      yield 2;
      yield 3;
    }
    expect([...LinkedList.from(numbers())]).toEqual([1, 2, 3]);
    expect(LinkedList.from([]).head).toBeNull();
    
  8. LinkedList.from(list) makes a separate copy
    const list = LinkedList.from([1, 2]);
    const copy = LinkedList.from(list);
    expect(copy).not.toBe(list);
    copy.push(3);
    expect([...list]).toEqual([1, 2]);
    expect([...copy]).toEqual([1, 2, 3]);
    

Learn more

solution.jsSaved in this browser

Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.

Ctrl + Enter runs the tests (⌘ + Enter on a Mac).