Claude Code for bisect: Binary Search in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for bisect: Binary Search in Python
AI

Claude Code for bisect: Binary Search in Python

Published: July 22, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s bisect module provides binary search on sorted lists. import bisect. bisect_left: bisect.bisect_left(a, x) → index where x would be inserted to keep a sorted; if x present, index of leftmost x. bisect_right: bisect.bisect_right(a, x) (alias bisect.bisect) → index after rightmost x. insort_left: bisect.insort_left(a, x) — insert x maintaining sort. insort_right: bisect.insort_right(a, x) (alias bisect.insort). key: bisect.bisect_left(a, x, key=fn) (Python 3.10+) — compare via key(a[i]) vs x. Search pattern: i = bisect_left(a, x); a[i] == x → found. Range count: bisect_right(a, hi) - bisect_left(a, lo). Nearest: a[min(bisect_left(a,x), len(a)-1)]. Step function: grades = [60,70,80,90]; letters = “FDCBA”; letters[bisect_right(grades, score)]. insort is O(n) due to list insert; bisect_left/right is O(log n). Works on any sorted sequence supporting __len__ and __getitem__. Claude Code generates rate-table lookups, range queries, sorted-insert pipelines, and scheduling event finders.

CLAUDE.md for bisect

## bisect Stack
- Stdlib: import bisect
- Find:   i = bisect.bisect_left(sorted_list, value)  — O(log n)
- Exact:  if i < len(a) and a[i] == value: found
- Range:  count = bisect.bisect_right(a, hi) - bisect.bisect_left(a, lo)
- Insert: bisect.insort(a, value)  — O(n) due to list shift
- Key:    bisect.bisect_left(a, x, key=lambda e: e.score)  — Python 3.10+

bisect Binary Search Pipeline

# app/searchutil.py — binary search, range queries, step tables, nearest
from __future__ import annotations

import bisect
from dataclasses import dataclass
from typing import Any, Callable, Sequence, TypeVar

T = TypeVar("T")
K = TypeVar("K")


# ─────────────────────────────────────────────────────────────────────────────
# 1. Binary search helpers
# ─────────────────────────────────────────────────────────────────────────────

def search(sorted_list: list, value: Any) -> int | None:
    """
    Return index of value in sorted_list, or None if not found.

    Example:
        search([1, 3, 5, 7, 9], 5)   # 2
        search([1, 3, 5, 7, 9], 4)   # None
    """
    i = bisect.bisect_left(sorted_list, value)
    if i < len(sorted_list) and sorted_list[i] == value:
        return i
    return None


def contains(sorted_list: list, value: Any) -> bool:
    """
    Return True if value is in sorted_list using binary search.

    Example:
        contains([1, 3, 5, 7, 9], 5)  # True
        contains([1, 3, 5, 7, 9], 4)  # False
    """
    i = bisect.bisect_left(sorted_list, value)
    return i < len(sorted_list) and sorted_list[i] == value


def insertion_point(sorted_list: list, value: Any, side: str = "left") -> int:
    """
    Return the index where value should be inserted to maintain sort order.
    side="left" puts value before any existing equal values; "right" puts after.

    Example:
        insertion_point([1, 3, 3, 5], 3, "left")   # 1
        insertion_point([1, 3, 3, 5], 3, "right")  # 3
    """
    if side == "left":
        return bisect.bisect_left(sorted_list, value)
    return bisect.bisect_right(sorted_list, value)


# ─────────────────────────────────────────────────────────────────────────────
# 2. Range queries
# ─────────────────────────────────────────────────────────────────────────────

def count_in_range(
    sorted_list: list,
    lo: Any,
    hi: Any,
    inclusive: tuple[bool, bool] = (True, True),
) -> int:
    """
    Count elements in sorted_list within [lo, hi].

    Example:
        count_in_range([1,2,3,4,5,6,7,8,9,10], 3, 7)   # 5
        count_in_range([1,2,2,3,3,3,4], 2, 3)            # 5
    """
    lo_fn = bisect.bisect_left if inclusive[0] else bisect.bisect_right
    hi_fn = bisect.bisect_right if inclusive[1] else bisect.bisect_left
    return hi_fn(sorted_list, hi) - lo_fn(sorted_list, lo)


def slice_range(
    sorted_list: list,
    lo: Any,
    hi: Any,
    inclusive: tuple[bool, bool] = (True, True),
) -> list:
    """
    Return elements in sorted_list within [lo, hi].

    Example:
        slice_range([1,2,3,4,5,6,7], 3, 5)   # [3, 4, 5]
    """
    lo_fn = bisect.bisect_left if inclusive[0] else bisect.bisect_right
    hi_fn = bisect.bisect_right if inclusive[1] else bisect.bisect_left
    return sorted_list[lo_fn(sorted_list, lo): hi_fn(sorted_list, hi)]


def nearest(sorted_list: list[float], value: float) -> float:
    """
    Return the element in sorted_list closest to value.

    Example:
        nearest([1.0, 3.0, 6.0, 10.0], 4.0)   # 3.0
        nearest([1.0, 3.0, 6.0, 10.0], 5.0)   # 6.0
    """
    if not sorted_list:
        raise ValueError("Empty list")
    i = bisect.bisect_left(sorted_list, value)
    candidates = []
    if i < len(sorted_list):
        candidates.append(sorted_list[i])
    if i > 0:
        candidates.append(sorted_list[i - 1])
    return min(candidates, key=lambda x: abs(x - value))


def floor_value(sorted_list: list, value: Any) -> Any | None:
    """
    Return the largest element ≤ value, or None if all elements > value.

    Example:
        floor_value([1, 3, 5, 7, 9], 6)   # 5
        floor_value([1, 3, 5, 7, 9], 0)   # None
    """
    i = bisect.bisect_right(sorted_list, value)
    return sorted_list[i - 1] if i > 0 else None


def ceiling_value(sorted_list: list, value: Any) -> Any | None:
    """
    Return the smallest element ≥ value, or None if all elements < value.

    Example:
        ceiling_value([1, 3, 5, 7, 9], 4)   # 5
        ceiling_value([1, 3, 5, 7, 9], 9)   # 9
        ceiling_value([1, 3, 5, 7, 9], 10)  # None
    """
    i = bisect.bisect_left(sorted_list, value)
    return sorted_list[i] if i < len(sorted_list) else None


# ─────────────────────────────────────────────────────────────────────────────
# 3. Step-function / lookup tables
# ─────────────────────────────────────────────────────────────────────────────

def step_lookup(
    breakpoints: list,
    values: list,
    query: Any,
) -> Any:
    """
    Step-function lookup: return values[i] where breakpoints[i-1] <= query < breakpoints[i].
    len(values) must be len(breakpoints) + 1.

    Example:
        # Grade buckets: <60=F, <70=D, <80=C, <90=B, >=90=A
        grade = step_lookup([60, 70, 80, 90], ["F","D","C","B","A"], 83)  # "B"
    """
    if len(values) != len(breakpoints) + 1:
        raise ValueError("len(values) must be len(breakpoints)+1")
    i = bisect.bisect_right(breakpoints, query)
    return values[i]


def rate_table_lookup(
    thresholds: list[float],
    rates: list[float],
    amount: float,
) -> float:
    """
    Look up a tiered rate for amount.

    Example:
        # Shipping: $0–$25: $5.99, $25–$50: $4.99, $50+: free
        rate = rate_table_lookup([25.0, 50.0], [5.99, 4.99, 0.0], 35.0)  # 4.99
    """
    return step_lookup(thresholds, rates, amount)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Sorted list maintenance
# ─────────────────────────────────────────────────────────────────────────────

class SortedInsertList(list):
    """
    A list that maintains sorted order on insort() calls.
    Subclasses list for full compatibility.

    Example:
        sl = SortedInsertList()
        sl.insort(5)
        sl.insort(2)
        sl.insort(8)
        print(sl)   # [2, 5, 8]
    """

    def insort(self, value: Any, side: str = "right") -> None:
        """Insert value maintaining sorted order. O(n) due to list shift."""
        if side == "left":
            bisect.insort_left(self, value)
        else:
            bisect.insort_right(self, value)

    def search(self, value: Any) -> int | None:
        return search(self, value)

    def slice_range(self, lo: Any, hi: Any) -> list:
        return slice_range(self, lo, hi)

    def count_range(self, lo: Any, hi: Any) -> int:
        return count_in_range(self, lo, hi)


# ─────────────────────────────────────────────────────────────────────────────
# 5. Key-based binary search (Python 3.10+ native, fallback for older)
# ─────────────────────────────────────────────────────────────────────────────

@dataclass(order=True)
class SortedItem:
    """Wrapper to enable bisect with a sort key on arbitrary objects."""
    key: Any
    value: Any = None


def search_by_key(
    sorted_items: list[Any],
    key_fn: Callable[[Any], Any],
    target_key: Any,
) -> int | None:
    """
    Binary search on a list sorted by key_fn(item).
    Returns index of first match or None.

    Example:
        events = sorted(events, key=lambda e: e.timestamp)
        idx = search_by_key(events, lambda e: e.timestamp, target_ts)
    """
    keys = [SortedItem(key=key_fn(item)) for item in sorted_items]
    target = SortedItem(key=target_key)
    i = bisect.bisect_left(keys, target)
    if i < len(sorted_items) and key_fn(sorted_items[i]) == target_key:
        return i
    return None


def count_by_key_range(
    sorted_items: list[Any],
    key_fn: Callable[[Any], Any],
    lo_key: Any,
    hi_key: Any,
) -> int:
    """
    Count items where lo_key <= key_fn(item) <= hi_key.

    Example:
        # Count events between two timestamps
        count = count_by_key_range(events, lambda e: e.ts, start_ts, end_ts)
    """
    keys   = [SortedItem(key_fn(item)) for item in sorted_items]
    lo_idx = bisect.bisect_left(keys, SortedItem(lo_key))
    hi_idx = bisect.bisect_right(keys, SortedItem(hi_key))
    return hi_idx - lo_idx


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== bisect demo ===")

    a = [1, 3, 3, 5, 7, 9, 11]

    print("\n--- search / contains ---")
    print(f"  search(a, 5):  {search(a, 5)}")
    print(f"  search(a, 4):  {search(a, 4)}")
    print(f"  contains(a, 3): {contains(a, 3)}")
    print(f"  contains(a, 4): {contains(a, 4)}")

    print("\n--- insertion_point ---")
    print(f"  bisect_left(a, 3):  {insertion_point(a, 3, 'left')}")
    print(f"  bisect_right(a, 3): {insertion_point(a, 3, 'right')}")

    print("\n--- count_in_range / slice_range ---")
    nums = list(range(1, 21))
    print(f"  count [5, 15]: {count_in_range(nums, 5, 15)}")
    print(f"  slice [5, 10]: {slice_range(nums, 5, 10)}")
    print(f"  count (5, 15) exclusive hi: {count_in_range(nums, 5, 15, (True, False))}")

    print("\n--- nearest / floor / ceiling ---")
    vals = [1.0, 3.0, 6.0, 10.0]
    for q in [0.5, 2.0, 4.5, 7.0, 12.0]:
        print(f"  q={q:5.1f}  nearest={nearest(vals, q)!r:5}  "
              f"floor={floor_value(vals, q)!r:5}  ceiling={ceiling_value(vals, q)!r}")

    print("\n--- step_lookup (grades) ---")
    breakpoints = [60, 70, 80, 90]
    labels      = ["F", "D", "C", "B", "A"]
    for score in [55, 62, 75, 83, 91]:
        grade = step_lookup(breakpoints, labels, score)
        print(f"  score={score}  grade={grade}")

    print("\n--- rate_table_lookup (shipping) ---")
    for amount in [10.0, 30.0, 60.0]:
        rate = rate_table_lookup([25.0, 50.0], [5.99, 4.99, 0.0], amount)
        print(f"  ${amount:.2f} → shipping ${rate:.2f}")

    print("\n--- SortedInsertList ---")
    sl = SortedInsertList()
    for v in [5, 2, 8, 1, 9, 3]:
        sl.insort(v)
    print(f"  sorted:    {list(sl)}")
    print(f"  range [2,6]: {sl.slice_range(2, 6)}")
    print(f"  count [2,8]: {sl.count_range(2, 8)}")

    print("\n=== done ===")

For the sortedcontainers.SortedList alternative — sortedcontainers.SortedList (PyPI) wraps a sorted list with O(log n) insertions AND O(log n) deletions using a clever B-tree-like internal structure, while exposing all standard list operations; stdlib bisect.insort is O(n) for insertion due to list shifting — use SortedList when insertion throughput matters (many writes into a maintained sorted collection), stdlib bisect for read-heavy lookup patterns where the data is written once and queried many times (rate tables, grade buckets, static event schedules). For the numpy.searchsorted alternative — numpy.searchsorted(a, v) performs the same binary search on NumPy arrays with vectorized batch queries: np.searchsorted(sorted_array, [val1, val2, val3]) returns three indices in one call; stdlib bisect operates on Python lists and handles one query at a time — use numpy.searchsorted for batch lookups over large numerical arrays in data science pipelines, stdlib bisect for Python-native sorted lists, custom comparison logic, and zero-dependency environments. The Claude Skills 360 bundle includes bisect skill sets covering search()/contains()/insertion_point() search helpers, count_in_range()/slice_range()/nearest()/floor_value()/ceiling_value() range query utilities, step_lookup()/rate_table_lookup() step-function tables, SortedInsertList maintained-sort list subclass, and search_by_key()/count_by_key_range() key-based binary search. Start with the free tier to try binary search patterns and bisect algorithm code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free