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
The platform team publishes a latency histogram per service, and the bucket boundaries come from a config file that people edit by hand. They are not sorted, the same boundary sometimes appears twice, and the measurements themselves occasionally carry a string like timeout instead of a number.
Write bucket_latencies(values, boundaries). Return the buckets in order plus a count of what could not be measured.
Function to write
bucket_latencies(values: list, boundaries: list) -> dictA dict with buckets — each a label and a count, in ascending order — and skipped.
How to approach it
Sort and deduplicate the edges first; then each value's bucket is a search, not a scan.
Sample cases
+ 2 held back until you submit
a latency histogram
Boundaries out of order, values exactly on two edges, one above every edge, and two unusable.
Input
Argument 1
[
12,
250,
99.5,
1000,
1001,
250,
0,
-5,
'timeout',
None,
500
]Argument 2
[
1000,
100,
250
]Returns
{
'buckets': [
{
'label': '<= 100',
'count': 4
},
{
'label': '100 - 250',
'count': 2
},
{
'label': '250 - 1000',
'count': 2
},
{
'label': '> 1000',
'count': 1
}
],
'skipped': 2
}nothing measured
No values still produces every bucket, each with a count of zero.
Input
Argument 1
[] (empty list)
Argument 2
[
100,
250
]Returns
{
'buckets': [
{
'label': '<= 100',
'count': 0
},
{
'label': '100 - 250',
'count': 0
},
{
'label': '> 250',
'count': 0
}
],
'skipped': 0
}no boundaries
With no edges declared there is one bucket holding everything.
Input
Argument 1
[
1,
2,
3
]Argument 2
[] (empty list)
Returns
{
'buckets': [
{
'label': '<= None',
'count': 3
}
],
'skipped': 0
}Constraints
skipped and goes in no bucket.label and a count. The first reads <= bound, the middle ones lower - upper, and the last > bound.Worked example
The config lists the boundaries as 1000, 100, 250. Sorted, they describe four buckets: up to 100, 100 to 250, 250 to 1000, and above 1000. The value 250 sits exactly on an edge and belongs to the bucket that ends there, so it counts in 100 - 250 — twice, because it appears twice in the data.
The value 1001 is above every boundary. Without the extra overflow bucket it has nowhere to go, and the two ways people handle that silently are both wrong: dropping it understates the tail, and folding it into the last named bucket makes a bucket labelled 250 - 1000 contain a value of 1001. The tail is the part of a latency histogram anyone actually reads.
What this tests
Boundary semantics stated precisely enough to implement, and the habit of normalising configuration before trusting it. Off-by-one at a bucket edge is invisible in aggregate and obvious to whoever is on call.
bucket_latencies(values: list, boundaries: list) -> 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.