A nightly meter import yields one optional reading per row; what does inverting that list of optionals into one optional list give you?
answer
- same values, different nesting
- one decision for the whole batch
- present only if every row present
- order and length preserved
- empty input gives present empty list
basics
~20 sInverting a list of optional readings yields one optional list: present only when every row was present, holding all readings in order. One missing row makes the whole result missing, so the caller unwraps once, not per row.
solid answer
~40 sThe inversion turns a *list of optional readings* into an *optional list of readings*. The result holds a value exactly when every row held one, and that value is the full list in the original order; if any row is empty, the whole result is empty. That is the payoff: the caller stops carrying a per-row context and makes one decision about the batch - proceed with all readings, or handle absence once. An empty input list inverts to a present, empty list, because no row failed to supply a value. The same move works when each row carries a failure description instead of mere absence: a list of fallible results becomes one fallible result holding the list.
code
pseudocode · 7 linesfunction invert(items): // items: list of optional<T>
out = empty list
for each item in items:
if item is empty:
return empty optional // one empty row decides the batch
out.append(value of item)
return optional of out // present, holds every value in ordergo deeper
Recall the shape change itself: a list of optional readings becomes one optional list of readings, and it is present only when every row had a value.
Explain the guarantees - presence only if all present, order and length preserved, empty input giving a present empty list - and why one unwrap beats one per element.
Show when the all-or-nothing contract is the right one for a batch, and name what you give up: the good rows, and any record of which row was missing.
Decide which shape shared import tooling should publish - all-or-nothing or a partition - and what other teams then have to build around the choice you standardise.
## Two shapes, the same values A nightly import that reads one meter reading per row naturally produces a **list of wrapped values**: for every row, a context that either holds a reading or is empty. That collection answers a per-row question, one row at a time. What the code downstream usually wants is the batch-level question - *did the whole night's file give me readings?* The inversion (commonly called *sequence*) changes only the nesting: - **Before** - a list of optional readings: `n` contexts, `n` decisions, one per row. - **After** - an optional list of readings: one context, one decision for the batch. No reading is transformed and none is added. The values inside are the same values. ## What the inverted shape promises 1. **Presence is conjunctive.** The result holds a value exactly when every element held one. One empty row is enough to make the whole result empty. 2. **Order is preserved.** The readings appear in the order their rows did; the inversion is not a sort and not a set operation. 3. **Length is preserved.** When the result is present, it holds exactly as many readings as there were rows. 4. **The empty input is a present, empty list.** Zero rows means no row failed to supply a value, so the honest answer is "here is your collection: it is empty", not "there is nothing here". ## Before and after, side by side | | list of optional readings | optional list of readings | |---|---|---| | what the caller unwraps | one context per row | one context, once | | question it answers | did *this* row parse? | did the *batch* parse? | | shape passed downstream | every consumer re-checks each element | a plain list of readings | | partial success | representable | not representable | | which row was missing | visible per element | not carried by an optional context | ## Why callers want it - **One decision point.** Import code that takes a plain list of readings needs no per-element checking; the absence case is handled once, at the boundary. - **It matches the contract of a batch.** A nightly file that is meant to load as a unit is honestly described by "all readings or none", and the inverted shape is exactly that statement in the type. - **It composes.** Once the batch is one wrapped value, it chains with the next step - persist, aggregate, report - the same way a single row would. - **It stops the shape from spreading.** Without the inversion, the per-row context leaks into every function the collection is passed to. ## What the shape costs you - **Good rows are discarded.** If 999 rows of 1,000 held readings, the present ones are thrown away with the absent one. That is the contract, not a bug - but it has to be the contract you wanted. - **An optional context carries no explanation.** You learn that something was missing, not which row or why. When the operator needs that, the per-row context has to carry a failure description instead of mere absence, and the inversion then produces one described failure rather than a bare empty result. - **It is all-or-nothing by construction.** Returning the good rows *and* the bad ones is a different operation - a partition - that you choose deliberately. - **A fail-fast implementation may stop early.** Once the first empty row is seen, the answer is determined, so an implementation is free to return without examining the rest. That is an optimisation of the same result, not a different one. ## Where candidates go wrong - **Confusing it with filtering.** Filtering drops the empty rows and returns a shorter list that reports nothing. The inversion returns everything or nothing and reports the difference. - **Guessing that an empty batch inverts to an empty context.** It does not: with no rows, nothing failed, so the result is present and holds an empty list. - **Expecting the missing row's index.** With a bare optional context there is no place to put it. - **Assuming the readings come back in some canonical order.** The inversion preserves the input order and does nothing else to it.
- What changes if each row carries a described failure instead of a bare absence?The shape becomes one fallible result holding the whole list, and the failure side now carries something reportable. A fail-fast inversion keeps the first failure it meets, so you learn about one bad row rather than all of them unless the failures are deliberately combined.
- Is inverting a list of optional readings the same as filtering out the empty rows?No. Filtering returns a shorter list and reports nothing about what it dropped, so a caller cannot tell a complete batch from a half-empty one. The inversion returns either every reading or none, and the empty result is the report.
- A thousand-row batch has one empty row; how many readings does the inverted result hold?None. The result is the empty context, and the 999 present readings are discarded with it. If keeping them matters, the operation you want is a partition into accepted and rejected rows, not an inversion.
saying these in an interview costs you the question
- Thinks the inversion drops the missing rows and returns the rest
- Says an empty input list inverts to the empty context
- Claims the result tells you which row was missing
- Believes the inversion reorders or deduplicates the readings
- Assumes partial success is representable in the inverted shape