Design layer document with undo, redo and batches
You are building the core of a design tool. A document holds layers. Each layer has a unique integer id and a set of string properties, such as color, shape or size. A layer does not need to be created first. It exists from the first time a property is set on its id.
Implement a Document class. The interview usually adds parts one at a time.
Part 1: edit, read, undo
-
Document()creates an empty document. -
apply(layer_id: int, key: str, value: str) -> Nonesetskeytovalueon that layer. It creates the layer if the layer does not exist. -
get_layer(layer_id: int) -> dict[str, str] | Nonereturns the layer's properties, orNoneif the layer does not exist. Changing the returned dict must not change the document. -
undo() -> boolreverts the most recent change that has not been undone yet, and returnsTrue. If there is nothing to undo, it returnsFalseand does nothing. If undo reverts the change that created a layer, the layer no longer exists.
Part 2: redo
-
redo() -> boolapplies the most recently undone change again. It returnsFalseif there is nothing to redo. - Any new
applyafter an undo throws away everything that could have been redone.
Part 3: batches
-
begin_batch()starts a group.commit_batch()ends it. - Edits inside a batch take effect immediately. After the commit, the whole batch is a single step for
undoandredo. - Committing an empty batch creates no step. Calling
undoorredowhile a batch is open is an error.
Constraints
- Up to
10^5operations in total, and documents with10^4or more layers. -
apply,undoandredoshould not take time proportional to the size of the whole document.
Example 1
d = Document()
d.apply(7, "fill", "green")
d.apply(7, "fill", "black")
d.apply(7, "radius", "4")
d.get_layer(7) -> {"fill": "black", "radius": "4"}
d.undo() -> True # {"fill": "black"}
d.undo() -> True # {"fill": "green"}
d.undo() -> True # layer 7 no longer exists
d.get_layer(7) -> None
d.redo() -> True # {"fill": "green"}
d.apply(3, "opacity", "50")
d.redo() -> False # the new edit cleared redo history
Example 2
d = Document()
d.apply(1, "x", "0")
d.begin_batch()
d.apply(1, "x", "10")
d.apply(2, "y", "20")
d.commit_batch()
d.undo() -> True # layer 1 is {"x": "0"}, layer 2 is gone
d.redo() -> True # layer 1 is {"x": "10"}, layer 2 is {"y": "20"}
Sign in to see the worked answer
The full answer, chapter by chapter, with what interviewers expect at each level. Free with an account: one full answer per company.
