Sign in to run and submit your work
Reading is open to everyone. Running code and saving drafts need an account so your work is yours and comes back on your next visit.
or
CODE WORKSPACE
A source system has no change feed, so the pipeline takes a full snapshot every night and works out what moved by comparing it against yesterday's. The source also stamps every row with an updated_at on every export, whether or not anything about the row changed, which makes a naive comparison report the entire table as modified.
Write diff_snapshots(before, after, key, ignore_fields).
Function to write
diff_snapshots(before: list[dict], after: list[dict], key: str, ignore_fields: list[str]) -> dictA dict with added and removed keys, changed rows with their field-level differences, and an unchanged count.
How to approach it
Index both sides by key, then walk the union and compare field by field.
Sample cases
+ 2 held back until you submit
a night of changes
One row unchanged apart from its timestamp, one changed in two fields, one added and one removed.
Input
Argument 1
| id | name | tier | updated_at |
|---|---|---|---|
| c1 | Ada | gold | 2026-03-01T00:00:00Z |
| c2 | Grace | silver | 2026-03-01T00:00:00Z |
| c3 | Alan | gold | 2026-03-01T00:00:00Z |
Argument 2
| id | name | tier | updated_at |
|---|---|---|---|
| c1 | Ada | gold | 2026-03-02T00:00:00Z |
| c2 | Grace Hopper | gold | 2026-03-02T00:00:00Z |
| c4 | Edsger | silver | 2026-03-02T00:00:00Z |
Argument 3
'id'Argument 4
[
'updated_at'
]Returns
{
'added': [
'c4'
],
'removed': [
'c3'
],
'changed': [
{
'key': 'c2',
'fields': {
'name': {
'before': 'Grace',
'after': 'Grace Hopper'
},
'tier': {
'before': 'silver',
'after': 'gold'
}
}
}
],
'unchanged': 1
}nothing before
A first snapshot makes every row an addition rather than a change.
Input
Argument 1
[] (empty list)
Argument 2
| id | name | tier | updated_at |
|---|---|---|---|
| c1 | Ada | gold | 2026-03-02T00:00:00Z |
| c2 | Grace Hopper | gold | 2026-03-02T00:00:00Z |
| c4 | Edsger | silver | 2026-03-02T00:00:00Z |
Argument 3
'id'Argument 4
[
'updated_at'
]Returns
{
'added': [
'c1',
'c2',
'c4'
],
'removed': [],
'changed': [],
'unchanged': 0
}ignoring nothing
Without the ignore list the volatile timestamp makes every surviving row look changed.
Input
Argument 1
| id | name | tier | updated_at |
|---|---|---|---|
| c1 | Ada | gold | 2026-03-01T00:00:00Z |
| c2 | Grace | silver | 2026-03-01T00:00:00Z |
| c3 | Alan | gold | 2026-03-01T00:00:00Z |
Argument 2
| id | name | tier | updated_at |
|---|---|---|---|
| c1 | Ada | gold | 2026-03-02T00:00:00Z |
| c2 | Grace Hopper | gold | 2026-03-02T00:00:00Z |
| c4 | Edsger | silver | 2026-03-02T00:00:00Z |
Argument 3
'id'Argument 4
[] (empty list)
Returns
{
'added': [
'c4'
],
'removed': [
'c3'
],
'changed': [
{
'key': 'c1',
'fields': {
'updated_at': {
'before': '2026-03-01T00:00:00Z',
'after': '2026-03-02T00:00:00Z'
}
}
},
{
'key': 'c2',
'fields': {
'name': {
'before': 'Grace',
'after': 'Grace Hopper'
},
'tier': {
'before': 'silver',
'after': 'gold'
},
'updated_at': {
'before': '2026-03-01T00:00:00Z',
'after': '2026-03-02T00:00:00Z'
}
}
}
],
'unchanged': 0
}Constraints
after is added; only in before is removed.ignore_fields.None or to None. An absent field and a null field are the same thing here, because the snapshot cannot tell them apart.True == 1.before and after.Worked example
c1 is byte-for-byte the same except that updated_at moved from the first of March to the second. With updated_at ignored it is counted as unchanged; without it, the diff reports a change on every row in the table every night, and the downstream job that reloads changed rows reloads everything.
c2 changed in two fields at once — the name became Grace Hopper and the tier became gold. Reporting the row as 'changed' with no detail leaves whoever is debugging to fetch both snapshots and compare by hand, which is the work this function was written to avoid. The field-level detail is the deliverable, not the row-level verdict.
What this tests
Diffing at the right granularity, and the judgement that some columns carry no information about change. The volatile-column problem is the single most common reason a snapshot diff is useless in practice.
diff_snapshots(before: list[dict], after: list[dict], key: str, ignore_fields: list[str]) -> dictSubmit for review to find out what your query gets right, what it gets wrong, and how it compares with the best working query for this exercise.
This scenario runs a full workspace — editor, canvas and results side by side. It needs a laptop or desktop to be usable. Open this page on a bigger screen to start building.