Current section

Files

Jump to
gleam_stdlib src gleam set.gleam
Raw

src/gleam/set.gleam

import gleam/map.{Map}
import gleam/result
import gleam/list
if erlang {
// A list is used as the map value as an empty list has the smallest
// representation in Erlang's binary format
type Token =
List(Nil)
const token = []
}
if javascript {
type Token =
Nil
const token = Nil
}
/// A set is a collection of unique members of the same type.
///
/// It is implemented using the `gleam/map` module, so inserts and lookups have
/// logarithmic time complexity.
///
pub opaque type Set(member) {
Set(map: Map(member, Token))
}
/// Creates a new empty set.
///
pub fn new() -> Set(member) {
Set(map.new())
}
/// Gets the number of members in a set.
///
/// This function runs in constant time.
///
/// ## Examples
///
/// > new() |> insert(1) |> insert(2) |> size
/// 2
///
pub fn size(set: Set(member)) -> Int {
map.size(set.map)
}
/// Inserts an member into the set.
///
/// This function runs in logarithmic time.
///
/// ## Examples
///
/// > new() |> insert(1) |> insert(2) |> size
/// 2
///
pub fn insert(into set: Set(member), this member: member) -> Set(member) {
Set(map: map.insert(set.map, member, token))
}
/// Checks whether a set contains a given member.
///
/// This function runs in logarithmic time.
///
/// ## Examples
///
/// > new() |> insert(2) |> contains(2)
/// True
///
/// > new() |> insert(2) |> contains(1)
/// False
///
pub fn contains(in set: Set(member), this member: member) -> Bool {
set.map
|> map.get(member)
|> result.is_ok
}
/// Removes a member from a set. If the set does not contain the member then
/// the set is returned unchanged.
///
/// This function runs in logarithmic time.
///
/// ## Examples
///
/// > new() |> insert(2) |> delete(2) |> contains(1)
/// False
///
pub fn delete(from set: Set(member), this member: member) -> Set(member) {
Set(map: map.delete(set.map, member))
}
/// Converts the set into a list of the contained members.
///
/// The list has no specific ordering, any unintentional ordering may change in
/// future versions of Gleam or Erlang.
///
/// This function runs in linear time.
///
/// ## Examples
///
/// > new() |> insert(2) |> to_list
/// [2]
///
pub fn to_list(set: Set(member)) -> List(member) {
map.keys(set.map)
}
/// Creates a new set of the members in a given list.
///
/// This function runs in loglinear time.
///
/// ## Examples
///
/// > import gleam/list
/// > [1, 1, 2, 4, 3, 2] |> from_list |> to_list |> list.sort
/// [1, 3, 3, 4]
///
pub fn from_list(members: List(member)) -> Set(member) {
let map =
list.fold(
over: members,
from: map.new(),
with: fn(m, k) { map.insert(m, k, token) },
)
Set(map)
}
/// Combines all entries into a single value by calling a given function on each
/// one.
///
/// Sets are not ordered so the values are not returned in any specific order.
/// Do not write code that relies on the order entries are used by this
/// function as it may change in later versions of Gleam or Erlang.
///
/// # Examples
///
/// > from_list([1, 3, 9])
/// > |> fold(0, fn(member, accumulator) { accumulator + member })
/// 13
///
pub fn fold(
over set: Set(member),
from initial: acc,
with reducer: fn(acc, member) -> acc,
) -> acc {
map.fold(over: set.map, from: initial, with: fn(a, k, _) { reducer(a, k) })
}
/// Creates a new set from an existing set, minus any members that a given
/// function returns `False` for.
///
/// This function runs in loglinear time.
///
/// ## Examples
///
/// > import gleam/int
/// > from_list([1, 4, 6, 3, 675, 44, 67])
/// > |> filter(for: int.is_even)
/// > |> to_list
/// [4, 6, 44]
///
pub fn filter(
in set: Set(member),
for property: fn(member) -> Bool,
) -> Set(member) {
Set(map.filter(in: set.map, for: fn(m, _) { property(m) }))
}
/// Creates a new map from a given map, only including any members which are in
/// a given list.
///
/// This function runs in loglinear time.
///
/// ## Examples
///
/// > from_list([1, 2, 3]) |> take([1, 3, 5]) |> to_list
/// [1, 3]
///
pub fn take(from set: Set(member), keeping desired: List(member)) -> Set(member) {
Set(map.take(from: set.map, keeping: desired))
}
fn order(first: Set(member), second: Set(member)) -> #(Set(member), Set(member)) {
case map.size(first.map) > map.size(second.map) {
True -> #(first, second)
False -> #(second, first)
}
}
/// Creates a new set that contains all members of both given sets.
///
/// This function runs in loglinear time.
///
/// ## Examples
///
/// > union(from_list([1, 2]), from_list([2, 3])) |> to_list
/// [1, 2, 3]
///
pub fn union(of first: Set(member), and second: Set(member)) -> Set(member) {
let #(larger, smaller) = order(first, second)
fold(over: smaller, from: larger, with: insert)
}
/// Creates a new set that contains members that are present in both given sets.
///
/// This function runs in loglinear time.
///
/// ## Examples
///
/// > intersection(from_list([1, 2]), from_list([2, 3])) |> to_list
/// [2]
///
pub fn intersection(
of first: Set(member),
and second: Set(member),
) -> Set(member) {
let #(larger, smaller) = order(first, second)
take(from: larger, keeping: to_list(smaller))
}