///|
/// An ordered converter from string categories to numeric axis positions.
///
/// Categories are assigned positions in first-seen order. Repeated labels
/// reuse the position that was assigned to their first occurrence.
pub struct CategoryAxis {
labels : Array[String]
}
///|
/// Create an empty category converter.
pub fn category() -> CategoryAxis {
{ labels: [], }
}
///|
fn find_label(labels : Array[String], label : String) -> Int32? {
for i in 0.. CategoryAxis {
let (_, result) = self.encode(labels)
result
}
///|
/// Convert string values to stable numeric positions.
/// Empty strings are valid categories; the `String` element type rejects
/// non-string values before this function is called.
pub fn CategoryAxis::encode(
self : CategoryAxis,
values : Array[String],
) -> (Array[Double], CategoryAxis) {
let next : CategoryAxis = { labels: self.labels.copy(), }
let positions = Array(capacity=values.length())
for value in values {
let index = match find_label(next.labels, value) {
Some(index) => index
None => {
let index = next.labels.length()
next.labels.push(value)
index
}
}
positions.push(Double::from_int(index))
}
(positions, next)
}
///|
/// Return the numeric position for each known category.
pub fn CategoryAxis::tick_positions(self : CategoryAxis) -> Array[Double] {
let result = Array(capacity=self.labels.length())
for i in 0.. Array[String] {
self.labels.copy()
}
///|
/// Return all known labels in their stable first-seen order.
pub fn CategoryAxis::labels(self : CategoryAxis) -> Array[String] {
self.labels.copy()
}
///|
/// Return the number of known categories.
pub fn CategoryAxis::length(self : CategoryAxis) -> Int32 {
self.labels.length()
}