Collections¶
Collection is a fluent, chainable wrapper around a list of items. It provides dozens of methods for
mapping, filtering, reducing, and reshaping data — a far more expressive alternative to threading a
list through nested comprehensions.
Introduction¶
Wrap any iterable with the collect() helper:
from arvel.support import collect
names = (
collect([{"name": "Ada"}, {"name": "Grace"}, {"name": "Linus"}])
.pluck("name") # Collection(["Ada", "Grace", "Linus"])
.map(str.upper) # Collection(["ADA", "GRACE", "LINUS"])
.sort()
.all() # ["ADA", "GRACE", "LINUS"]
)
Almost every method returns a new Collection, so calls chain without mutating the source.
.all() (or .to_list()) unwraps back to a plain list.
Available methods¶
Transforming¶
collect(items).map(fn) # transform each item
collect(items).filter(fn) # keep items where fn is truthy
collect(items).reject(fn) # drop items where fn is truthy
collect(items).reduce(fn, initial) # fold to a single value
collect(items).each(fn) # side effect per item (returns self)
collect(items).flatten() # flatten one level of nesting
collect(items).chunk(3) # Collection of size-3 lists
Querying¶
collect(users).where("active", True) # rows matching a key/value
collect(users).where_in("role", ["a", "b"])
collect(users).where_null("deleted_at")
collect(users).first_where("email", "[email protected]")
collect(users).pluck("email") # extract one key
collect(users).contains(item) # membership (or predicate)
collect(users).sole(lambda u: u.admin) # the one match, or raise
Aggregating¶
collect(nums).sum() # or .sum("price") over dicts/objects
collect(nums).avg() # also .median() / .mode()
collect(nums).min() # and .max()
collect(users).count()
collect(users).count_by("role") # {"admin": 2, "user": 9}
collect(users).group_by("team") # {"blue": [...], "red": [...]}
collect(users).key_by("id") # {1: {...}, 2: {...}}
Ordering & slicing¶
collect(items).sort() # natural order
collect(items).sort_by("age") # by a key (sort_by_desc for reverse)
collect(items).unique()
collect(items).reverse()
collect(items).take(3) # first 3 (negative = last 3)
collect(items).skip(2).slice(0, 5)
collect(items).nth(2) # every 2nd item
collect(items).shuffle() / .random()
Partitioning & testing¶
active, inactive = collect(users).partition(lambda u: u.active)
collect(users).every(lambda u: u.active) # all match?
collect(users).is_empty()
Lazy collections¶
A Collection is eager — each step builds a new list. For a large or streaming source (a big
file, a generator, a paginated API) where you don't want the whole thing in memory, use a
LazyCollection: it chains map/filter/each/take as generators and only pulls items as the
final step consumes them. Start one from a generator/iterable directly, or .lazy() off a
Collection:
from arvel.support import LazyCollection
def read_rows(): # a generator — nothing loaded yet
with open("huge.csv") as f:
yield from f
first_ten_active = (
LazyCollection(read_rows)
.map(parse)
.filter(lambda r: r.active)
.take(10) # stops reading the file after 10 matches
.to_list()
)
collect(items).lazy().filter(...).first() # or drop into lazy mode from a Collection
take/first short-circuit — they stop consuming the source as soon as they have enough, so an
infinite or huge source is fine. Call .collect() (or .to_list()) to materialize back into an
eager Collection/list once the set is small.
See also¶
- Strings — the same fluent style over a single string.
- ORM: Collections — query results returned as collections of models.