← Dynamic array lesson
Lab

Dynamic array

Sign in to run your code

Read the brief and the starter here. With a free account you can edit it, run the sample, step through it and check your work, and your progress is kept.

// ---- Checker plumbing: you don't need to edit this. ----------------------

/**
 * The track's buffer: a fixed number of slots, read and written only through
 * get, set and take. The checker counts moves here: every set writes one cue
 * into a slot, whether it is a copy, a shift or a new cue. take empties a slot
 * and is not a move.
 */
class Slots {
  static moves = 0;
  private readonly cells: (string | undefined)[];

  constructor(size: number) {
    this.cells = new Array<string | undefined>(size).fill(undefined);
  }

  /** For the checker: a buffer of size slots already holding cues, not counted. */
  static of(cues: string[], size: number): Slots {
    const slots = new Slots(size);
    slots.cells.splice(0, cues.length, ...cues);
    return slots;
  }

  /** For the checker: the first count slots, read without a loop. */
  peek(count: number): (string | undefined)[] {
    return this.cells.slice(0, count);
  }

  get size(): number {
    return this.cells.length;
  }

  get(index: number): string | undefined {
    return this.cells[index];
  }

  set(index: number, cue: string | undefined): void {
    if (!(index >= 0 && index < this.cells.length)) throw new RangeError(`no slot ${index} in a buffer of ${this.cells.length}`);
    this.cells[index] = cue;
    Slots.moves++;
  }

  take(index: number): string | undefined {
    const cue = this.cells[index];
    this.cells[index] = undefined;
    return cue;
  }
}

// ---- End of checker plumbing. --------------------------------------------

/**
 * A caption track: the lesson's growing array, holding caption cues. Its
 * buffer is a Slots (above), which counts every cue written into it.
 */
class CaptionTrack {
  slots = new Slots(4);
  length = 0;

  get capacity(): number {
    return this.slots.size;
  }

  /** Copies the cues into a new buffer of the given size. */
  private resize(capacity: number): void {
    const newSlots = new Slots(capacity);
    for (let i = 0; i < this.length; i++) newSlots.set(i, this.slots.get(i));
    this.slots = newSlots;
  }

  append(cue: string): void {
    if (this.length === this.capacity) this.resize(this.capacity * 2);
    this.slots.set(this.length, cue);
    this.length++;
  }

  get(index: number): string | undefined {
    return index >= 0 && index < this.length ? this.slots.get(index) : undefined;
  }

  remove(index: number): string | undefined {
    if (index < 0 || index >= this.length) return undefined;
    const removed = this.slots.take(index);
    // Keep the order: move every later cue one place earlier.
    for (let i = index + 1; i < this.length; i++) this.slots.set(i - 1, this.slots.take(i));
    this.length--;
    return removed;
  }

  /** Puts a cue at index, moving the cues from there one place later. */
  insert(index: number, cue: string): boolean {
    // TODO: a missed caption belongs between two others.
    return false;
  }
}