Skip to main content

NaryRelation

Struct NaryRelation 

Source
pub struct NaryRelation<T: Ord> { /* private fields */ }
Expand description

Deterministic exact n-ary relation with a named schema. A deterministic exact n-ary relation with a named schema.

NaryRelation<T> is the first G2 building block for schema-aware relations. It stores column names explicitly and keeps rows in a BTreeSet so iteration order is deterministic.

In this initial G2 step, all cells in a row share the same value type T.

§Examples

use relmath::NaryRelation;

let mut passed = NaryRelation::new(["student", "course", "status"])?;

assert_eq!(passed.column_index("course")?, 1);
assert!(passed.insert_row(["Alice", "Math", "passed"])?);
assert!(passed.insert_row(["Alice", "Physics", "passed"])?);
assert!(passed.insert_row(["Bob", "Physics", "in_progress"])?);

let completed = passed.select(|row| row[2] == "passed");
let students = completed.project(["student"])?;
let renamed = students.rename("student", "learner")?;

assert_eq!(passed.arity(), 3);
assert!(completed.contains_row(&["Alice", "Math", "passed"]));
assert_eq!(renamed.schema(), &["learner".to_string()]);

Implementations§

Source§

impl<T: Ord> NaryRelation<T>

Source

pub fn new<I, S>(schema: I) -> Result<Self, NaryRelationError>
where I: IntoIterator<Item = S>, S: Into<String>,

Creates an empty n-ary relation with the given schema.

Column names must be unique and non-blank.

Source

pub fn from_rows<I, S, R, Row>( schema: I, rows: R, ) -> Result<Self, NaryRelationError>
where I: IntoIterator<Item = S>, S: Into<String>, R: IntoIterator<Item = Row>, Row: IntoIterator<Item = T>,

Creates an n-ary relation from a schema and rows.

Column names must be unique and non-blank, and every row must have the same arity as the schema.

Source

pub fn from_named_rows<I, S, R, K>( schema: I, rows: R, ) -> Result<Self, NaryRelationError>
where I: IntoIterator<Item = S>, S: Into<String>, R: IntoIterator<Item = BTreeMap<K, T>>, K: Into<String> + Ord,

Creates an n-ary relation from a schema and named rows.

This is the current std-only G2 onboarding boundary. Each input row must provide exactly the schema columns by name. The schema order stays explicit in the relation rather than being inferred from map iteration.

§Examples
use std::collections::BTreeMap;

use relmath::NaryRelation;

let progress = NaryRelation::from_named_rows(
    ["student", "course", "status"],
    [
        BTreeMap::from([
            ("course", "Math"),
            ("status", "passed"),
            ("student", "Alice"),
        ]),
        BTreeMap::from([
            ("course", "Physics"),
            ("status", "in_progress"),
            ("student", "Bob"),
        ]),
    ],
)?;

assert_eq!(
    progress.to_rows(),
    vec![
        vec!["Alice", "Math", "passed"],
        vec!["Bob", "Physics", "in_progress"],
    ]
);
Examples found in repository?
examples/curriculum.rs (lines 8-33)
7fn main() -> Result<(), relmath::NaryRelationError> {
8    let progress = NaryRelation::from_named_rows(
9        ["student", "course", "status"],
10        [
11            BTreeMap::from([
12                ("course", "Math"),
13                ("status", "passed"),
14                ("student", "Alice"),
15            ]),
16            BTreeMap::from([
17                ("course", "Physics"),
18                ("status", "passed"),
19                ("student", "Alice"),
20            ]),
21            BTreeMap::from([("course", "Math"), ("status", "passed"), ("student", "Bob")]),
22            BTreeMap::from([
23                ("course", "Physics"),
24                ("status", "in_progress"),
25                ("student", "Bob"),
26            ]),
27            BTreeMap::from([
28                ("course", "Math"),
29                ("status", "passed"),
30                ("student", "Cara"),
31            ]),
32        ],
33    )?;
34    let course_rooms = NaryRelation::from_named_rows(
35        ["course", "room"],
36        [
37            BTreeMap::from([("course", "Math"), ("room", "R101")]),
38            BTreeMap::from([("course", "Physics"), ("room", "R201")]),
39            BTreeMap::from([("course", "History"), ("room", "R301")]),
40        ],
41    )?;
42
43    let completed = progress
44        .select(|row| row[2] == "passed")
45        .project(["student", "course"])?;
46    let scheduled = completed.natural_join(&course_rooms);
47    let by_room = scheduled.group_by(["room"])?;
48    let room_r101 = by_room.group(&["R101"]).expect("expected R101 group");
49    let room_assignments = scheduled.project(["room", "student"])?;
50    let exported_assignments = room_assignments.to_named_rows();
51
52    assert!(completed.contains_row(&["Alice", "Math"]));
53    assert_eq!(
54        scheduled.schema(),
55        &[
56            "student".to_string(),
57            "course".to_string(),
58            "room".to_string(),
59        ]
60    );
61    assert_eq!(
62        scheduled.to_rows(),
63        vec![
64            vec!["Alice", "Math", "R101"],
65            vec!["Alice", "Physics", "R201"],
66            vec!["Bob", "Math", "R101"],
67            vec!["Cara", "Math", "R101"],
68        ]
69    );
70    assert_eq!(by_room.key_schema(), &["room".to_string()]);
71    assert_eq!(by_room.member_schema(), scheduled.schema());
72    assert_eq!(by_room.counts(), vec![(vec!["R101"], 3), (vec!["R201"], 1)]);
73    assert_eq!(
74        room_r101.to_rows(),
75        vec![
76            vec!["Alice", "Math", "R101"],
77            vec!["Bob", "Math", "R101"],
78            vec!["Cara", "Math", "R101"],
79        ]
80    );
81    assert_eq!(
82        room_assignments.to_rows(),
83        vec![
84            vec!["R101", "Alice"],
85            vec!["R101", "Bob"],
86            vec!["R101", "Cara"],
87            vec!["R201", "Alice"],
88        ]
89    );
90    assert_eq!(
91        exported_assignments[0],
92        BTreeMap::from([
93            ("room".to_string(), "R101"),
94            ("student".to_string(), "Alice"),
95        ])
96    );
97
98    println!("scheduled course completions: {:?}", scheduled.to_rows());
99
100    Ok(())
101}
Source

pub fn schema(&self) -> &[String]

Returns the schema column names in order.

Examples found in repository?
examples/curriculum.rs (line 54)
7fn main() -> Result<(), relmath::NaryRelationError> {
8    let progress = NaryRelation::from_named_rows(
9        ["student", "course", "status"],
10        [
11            BTreeMap::from([
12                ("course", "Math"),
13                ("status", "passed"),
14                ("student", "Alice"),
15            ]),
16            BTreeMap::from([
17                ("course", "Physics"),
18                ("status", "passed"),
19                ("student", "Alice"),
20            ]),
21            BTreeMap::from([("course", "Math"), ("status", "passed"), ("student", "Bob")]),
22            BTreeMap::from([
23                ("course", "Physics"),
24                ("status", "in_progress"),
25                ("student", "Bob"),
26            ]),
27            BTreeMap::from([
28                ("course", "Math"),
29                ("status", "passed"),
30                ("student", "Cara"),
31            ]),
32        ],
33    )?;
34    let course_rooms = NaryRelation::from_named_rows(
35        ["course", "room"],
36        [
37            BTreeMap::from([("course", "Math"), ("room", "R101")]),
38            BTreeMap::from([("course", "Physics"), ("room", "R201")]),
39            BTreeMap::from([("course", "History"), ("room", "R301")]),
40        ],
41    )?;
42
43    let completed = progress
44        .select(|row| row[2] == "passed")
45        .project(["student", "course"])?;
46    let scheduled = completed.natural_join(&course_rooms);
47    let by_room = scheduled.group_by(["room"])?;
48    let room_r101 = by_room.group(&["R101"]).expect("expected R101 group");
49    let room_assignments = scheduled.project(["room", "student"])?;
50    let exported_assignments = room_assignments.to_named_rows();
51
52    assert!(completed.contains_row(&["Alice", "Math"]));
53    assert_eq!(
54        scheduled.schema(),
55        &[
56            "student".to_string(),
57            "course".to_string(),
58            "room".to_string(),
59        ]
60    );
61    assert_eq!(
62        scheduled.to_rows(),
63        vec![
64            vec!["Alice", "Math", "R101"],
65            vec!["Alice", "Physics", "R201"],
66            vec!["Bob", "Math", "R101"],
67            vec!["Cara", "Math", "R101"],
68        ]
69    );
70    assert_eq!(by_room.key_schema(), &["room".to_string()]);
71    assert_eq!(by_room.member_schema(), scheduled.schema());
72    assert_eq!(by_room.counts(), vec![(vec!["R101"], 3), (vec!["R201"], 1)]);
73    assert_eq!(
74        room_r101.to_rows(),
75        vec![
76            vec!["Alice", "Math", "R101"],
77            vec!["Bob", "Math", "R101"],
78            vec!["Cara", "Math", "R101"],
79        ]
80    );
81    assert_eq!(
82        room_assignments.to_rows(),
83        vec![
84            vec!["R101", "Alice"],
85            vec!["R101", "Bob"],
86            vec!["R101", "Cara"],
87            vec!["R201", "Alice"],
88        ]
89    );
90    assert_eq!(
91        exported_assignments[0],
92        BTreeMap::from([
93            ("room".to_string(), "R101"),
94            ("student".to_string(), "Alice"),
95        ])
96    );
97
98    println!("scheduled course completions: {:?}", scheduled.to_rows());
99
100    Ok(())
101}
Source

pub fn arity(&self) -> usize

Returns the arity of the relation.

Source

pub fn column_index(&self, column: &str) -> Result<usize, NaryRelationError>

Returns the zero-based position of a named column.

§Examples
use relmath::NaryRelation;

let students = NaryRelation::<&str>::new(["student", "course"])?;

assert_eq!(students.column_index("student")?, 0);
assert_eq!(students.column_index("course")?, 1);
Source

pub fn insert_row<I>(&mut self, row: I) -> Result<bool, NaryRelationError>
where I: IntoIterator<Item = T>,

Inserts a row into the relation.

Returns true when the row was not already present.

Source

pub fn contains_row(&self, row: &[T]) -> bool

Returns true when the relation contains the given row.

Examples found in repository?
examples/curriculum.rs (line 52)
7fn main() -> Result<(), relmath::NaryRelationError> {
8    let progress = NaryRelation::from_named_rows(
9        ["student", "course", "status"],
10        [
11            BTreeMap::from([
12                ("course", "Math"),
13                ("status", "passed"),
14                ("student", "Alice"),
15            ]),
16            BTreeMap::from([
17                ("course", "Physics"),
18                ("status", "passed"),
19                ("student", "Alice"),
20            ]),
21            BTreeMap::from([("course", "Math"), ("status", "passed"), ("student", "Bob")]),
22            BTreeMap::from([
23                ("course", "Physics"),
24                ("status", "in_progress"),
25                ("student", "Bob"),
26            ]),
27            BTreeMap::from([
28                ("course", "Math"),
29                ("status", "passed"),
30                ("student", "Cara"),
31            ]),
32        ],
33    )?;
34    let course_rooms = NaryRelation::from_named_rows(
35        ["course", "room"],
36        [
37            BTreeMap::from([("course", "Math"), ("room", "R101")]),
38            BTreeMap::from([("course", "Physics"), ("room", "R201")]),
39            BTreeMap::from([("course", "History"), ("room", "R301")]),
40        ],
41    )?;
42
43    let completed = progress
44        .select(|row| row[2] == "passed")
45        .project(["student", "course"])?;
46    let scheduled = completed.natural_join(&course_rooms);
47    let by_room = scheduled.group_by(["room"])?;
48    let room_r101 = by_room.group(&["R101"]).expect("expected R101 group");
49    let room_assignments = scheduled.project(["room", "student"])?;
50    let exported_assignments = room_assignments.to_named_rows();
51
52    assert!(completed.contains_row(&["Alice", "Math"]));
53    assert_eq!(
54        scheduled.schema(),
55        &[
56            "student".to_string(),
57            "course".to_string(),
58            "room".to_string(),
59        ]
60    );
61    assert_eq!(
62        scheduled.to_rows(),
63        vec![
64            vec!["Alice", "Math", "R101"],
65            vec!["Alice", "Physics", "R201"],
66            vec!["Bob", "Math", "R101"],
67            vec!["Cara", "Math", "R101"],
68        ]
69    );
70    assert_eq!(by_room.key_schema(), &["room".to_string()]);
71    assert_eq!(by_room.member_schema(), scheduled.schema());
72    assert_eq!(by_room.counts(), vec![(vec!["R101"], 3), (vec!["R201"], 1)]);
73    assert_eq!(
74        room_r101.to_rows(),
75        vec![
76            vec!["Alice", "Math", "R101"],
77            vec!["Bob", "Math", "R101"],
78            vec!["Cara", "Math", "R101"],
79        ]
80    );
81    assert_eq!(
82        room_assignments.to_rows(),
83        vec![
84            vec!["R101", "Alice"],
85            vec!["R101", "Bob"],
86            vec!["R101", "Cara"],
87            vec!["R201", "Alice"],
88        ]
89    );
90    assert_eq!(
91        exported_assignments[0],
92        BTreeMap::from([
93            ("room".to_string(), "R101"),
94            ("student".to_string(), "Alice"),
95        ])
96    );
97
98    println!("scheduled course completions: {:?}", scheduled.to_rows());
99
100    Ok(())
101}
Source

pub fn iter(&self) -> impl Iterator<Item = &[T]>

Returns an iterator over the stored rows in deterministic order.

Source

pub fn to_rows(&self) -> Vec<Vec<T>>
where T: Clone,

Returns the rows as a deterministically ordered vector.

Examples found in repository?
examples/curriculum.rs (line 62)
7fn main() -> Result<(), relmath::NaryRelationError> {
8    let progress = NaryRelation::from_named_rows(
9        ["student", "course", "status"],
10        [
11            BTreeMap::from([
12                ("course", "Math"),
13                ("status", "passed"),
14                ("student", "Alice"),
15            ]),
16            BTreeMap::from([
17                ("course", "Physics"),
18                ("status", "passed"),
19                ("student", "Alice"),
20            ]),
21            BTreeMap::from([("course", "Math"), ("status", "passed"), ("student", "Bob")]),
22            BTreeMap::from([
23                ("course", "Physics"),
24                ("status", "in_progress"),
25                ("student", "Bob"),
26            ]),
27            BTreeMap::from([
28                ("course", "Math"),
29                ("status", "passed"),
30                ("student", "Cara"),
31            ]),
32        ],
33    )?;
34    let course_rooms = NaryRelation::from_named_rows(
35        ["course", "room"],
36        [
37            BTreeMap::from([("course", "Math"), ("room", "R101")]),
38            BTreeMap::from([("course", "Physics"), ("room", "R201")]),
39            BTreeMap::from([("course", "History"), ("room", "R301")]),
40        ],
41    )?;
42
43    let completed = progress
44        .select(|row| row[2] == "passed")
45        .project(["student", "course"])?;
46    let scheduled = completed.natural_join(&course_rooms);
47    let by_room = scheduled.group_by(["room"])?;
48    let room_r101 = by_room.group(&["R101"]).expect("expected R101 group");
49    let room_assignments = scheduled.project(["room", "student"])?;
50    let exported_assignments = room_assignments.to_named_rows();
51
52    assert!(completed.contains_row(&["Alice", "Math"]));
53    assert_eq!(
54        scheduled.schema(),
55        &[
56            "student".to_string(),
57            "course".to_string(),
58            "room".to_string(),
59        ]
60    );
61    assert_eq!(
62        scheduled.to_rows(),
63        vec![
64            vec!["Alice", "Math", "R101"],
65            vec!["Alice", "Physics", "R201"],
66            vec!["Bob", "Math", "R101"],
67            vec!["Cara", "Math", "R101"],
68        ]
69    );
70    assert_eq!(by_room.key_schema(), &["room".to_string()]);
71    assert_eq!(by_room.member_schema(), scheduled.schema());
72    assert_eq!(by_room.counts(), vec![(vec!["R101"], 3), (vec!["R201"], 1)]);
73    assert_eq!(
74        room_r101.to_rows(),
75        vec![
76            vec!["Alice", "Math", "R101"],
77            vec!["Bob", "Math", "R101"],
78            vec!["Cara", "Math", "R101"],
79        ]
80    );
81    assert_eq!(
82        room_assignments.to_rows(),
83        vec![
84            vec!["R101", "Alice"],
85            vec!["R101", "Bob"],
86            vec!["R101", "Cara"],
87            vec!["R201", "Alice"],
88        ]
89    );
90    assert_eq!(
91        exported_assignments[0],
92        BTreeMap::from([
93            ("room".to_string(), "R101"),
94            ("student".to_string(), "Alice"),
95        ])
96    );
97
98    println!("scheduled course completions: {:?}", scheduled.to_rows());
99
100    Ok(())
101}
Source

pub fn to_named_rows(&self) -> Vec<BTreeMap<String, T>>
where T: Clone,

Exports the relation as named rows.

The returned vector follows the relation’s deterministic row order. Each row is materialized as a BTreeMap, so key lookup is by column name rather than schema position. Preserve Self::schema separately when schema order matters to a downstream consumer.

§Examples
use std::collections::BTreeMap;

use relmath::NaryRelation;

let progress = NaryRelation::from_rows(
    ["student", "course"],
    [["Alice", "Math"], ["Bob", "Physics"]],
)?;

assert_eq!(
    progress.to_named_rows(),
    vec![
        BTreeMap::from([
            ("course".to_string(), "Math"),
            ("student".to_string(), "Alice"),
        ]),
        BTreeMap::from([
            ("course".to_string(), "Physics"),
            ("student".to_string(), "Bob"),
        ]),
    ]
);
Examples found in repository?
examples/curriculum.rs (line 50)
7fn main() -> Result<(), relmath::NaryRelationError> {
8    let progress = NaryRelation::from_named_rows(
9        ["student", "course", "status"],
10        [
11            BTreeMap::from([
12                ("course", "Math"),
13                ("status", "passed"),
14                ("student", "Alice"),
15            ]),
16            BTreeMap::from([
17                ("course", "Physics"),
18                ("status", "passed"),
19                ("student", "Alice"),
20            ]),
21            BTreeMap::from([("course", "Math"), ("status", "passed"), ("student", "Bob")]),
22            BTreeMap::from([
23                ("course", "Physics"),
24                ("status", "in_progress"),
25                ("student", "Bob"),
26            ]),
27            BTreeMap::from([
28                ("course", "Math"),
29                ("status", "passed"),
30                ("student", "Cara"),
31            ]),
32        ],
33    )?;
34    let course_rooms = NaryRelation::from_named_rows(
35        ["course", "room"],
36        [
37            BTreeMap::from([("course", "Math"), ("room", "R101")]),
38            BTreeMap::from([("course", "Physics"), ("room", "R201")]),
39            BTreeMap::from([("course", "History"), ("room", "R301")]),
40        ],
41    )?;
42
43    let completed = progress
44        .select(|row| row[2] == "passed")
45        .project(["student", "course"])?;
46    let scheduled = completed.natural_join(&course_rooms);
47    let by_room = scheduled.group_by(["room"])?;
48    let room_r101 = by_room.group(&["R101"]).expect("expected R101 group");
49    let room_assignments = scheduled.project(["room", "student"])?;
50    let exported_assignments = room_assignments.to_named_rows();
51
52    assert!(completed.contains_row(&["Alice", "Math"]));
53    assert_eq!(
54        scheduled.schema(),
55        &[
56            "student".to_string(),
57            "course".to_string(),
58            "room".to_string(),
59        ]
60    );
61    assert_eq!(
62        scheduled.to_rows(),
63        vec![
64            vec!["Alice", "Math", "R101"],
65            vec!["Alice", "Physics", "R201"],
66            vec!["Bob", "Math", "R101"],
67            vec!["Cara", "Math", "R101"],
68        ]
69    );
70    assert_eq!(by_room.key_schema(), &["room".to_string()]);
71    assert_eq!(by_room.member_schema(), scheduled.schema());
72    assert_eq!(by_room.counts(), vec![(vec!["R101"], 3), (vec!["R201"], 1)]);
73    assert_eq!(
74        room_r101.to_rows(),
75        vec![
76            vec!["Alice", "Math", "R101"],
77            vec!["Bob", "Math", "R101"],
78            vec!["Cara", "Math", "R101"],
79        ]
80    );
81    assert_eq!(
82        room_assignments.to_rows(),
83        vec![
84            vec!["R101", "Alice"],
85            vec!["R101", "Bob"],
86            vec!["R101", "Cara"],
87            vec!["R201", "Alice"],
88        ]
89    );
90    assert_eq!(
91        exported_assignments[0],
92        BTreeMap::from([
93            ("room".to_string(), "R101"),
94            ("student".to_string(), "Alice"),
95        ])
96    );
97
98    println!("scheduled course completions: {:?}", scheduled.to_rows());
99
100    Ok(())
101}
Source

pub fn group_by<I, S>( &self, columns: I, ) -> Result<GroupedRelation<T>, NaryRelationError>
where I: IntoIterator<Item = S>, S: AsRef<str>, T: Clone,

Groups the relation by the named key columns.

Group keys follow the exact order of columns. Empty key selections, duplicate key columns, and unknown columns are rejected by the current exact core.

Each member relation keeps the original relation schema and contains the subset of rows whose projected key matches the group key. Groups are stored in a BTreeMap, so group iteration order is deterministic.

The first exact aggregate over the grouped output is GroupedRelation::counts, which reports the number of stored rows in each group.

§Examples
use relmath::NaryRelation;

let completed = NaryRelation::from_rows(
    ["student", "course", "term"],
    [
        ["Alice", "Math", "Fall"],
        ["Alice", "Physics", "Fall"],
        ["Bob", "Math", "Spring"],
    ],
)?;

let grouped = completed.group_by(["term", "student"])?;

assert_eq!(
    grouped.key_schema(),
    &["term".to_string(), "student".to_string()]
);
assert_eq!(
    grouped.counts(),
    vec![(vec!["Fall", "Alice"], 2), (vec!["Spring", "Bob"], 1)]
);
Examples found in repository?
examples/curriculum.rs (line 47)
7fn main() -> Result<(), relmath::NaryRelationError> {
8    let progress = NaryRelation::from_named_rows(
9        ["student", "course", "status"],
10        [
11            BTreeMap::from([
12                ("course", "Math"),
13                ("status", "passed"),
14                ("student", "Alice"),
15            ]),
16            BTreeMap::from([
17                ("course", "Physics"),
18                ("status", "passed"),
19                ("student", "Alice"),
20            ]),
21            BTreeMap::from([("course", "Math"), ("status", "passed"), ("student", "Bob")]),
22            BTreeMap::from([
23                ("course", "Physics"),
24                ("status", "in_progress"),
25                ("student", "Bob"),
26            ]),
27            BTreeMap::from([
28                ("course", "Math"),
29                ("status", "passed"),
30                ("student", "Cara"),
31            ]),
32        ],
33    )?;
34    let course_rooms = NaryRelation::from_named_rows(
35        ["course", "room"],
36        [
37            BTreeMap::from([("course", "Math"), ("room", "R101")]),
38            BTreeMap::from([("course", "Physics"), ("room", "R201")]),
39            BTreeMap::from([("course", "History"), ("room", "R301")]),
40        ],
41    )?;
42
43    let completed = progress
44        .select(|row| row[2] == "passed")
45        .project(["student", "course"])?;
46    let scheduled = completed.natural_join(&course_rooms);
47    let by_room = scheduled.group_by(["room"])?;
48    let room_r101 = by_room.group(&["R101"]).expect("expected R101 group");
49    let room_assignments = scheduled.project(["room", "student"])?;
50    let exported_assignments = room_assignments.to_named_rows();
51
52    assert!(completed.contains_row(&["Alice", "Math"]));
53    assert_eq!(
54        scheduled.schema(),
55        &[
56            "student".to_string(),
57            "course".to_string(),
58            "room".to_string(),
59        ]
60    );
61    assert_eq!(
62        scheduled.to_rows(),
63        vec![
64            vec!["Alice", "Math", "R101"],
65            vec!["Alice", "Physics", "R201"],
66            vec!["Bob", "Math", "R101"],
67            vec!["Cara", "Math", "R101"],
68        ]
69    );
70    assert_eq!(by_room.key_schema(), &["room".to_string()]);
71    assert_eq!(by_room.member_schema(), scheduled.schema());
72    assert_eq!(by_room.counts(), vec![(vec!["R101"], 3), (vec!["R201"], 1)]);
73    assert_eq!(
74        room_r101.to_rows(),
75        vec![
76            vec!["Alice", "Math", "R101"],
77            vec!["Bob", "Math", "R101"],
78            vec!["Cara", "Math", "R101"],
79        ]
80    );
81    assert_eq!(
82        room_assignments.to_rows(),
83        vec![
84            vec!["R101", "Alice"],
85            vec!["R101", "Bob"],
86            vec!["R101", "Cara"],
87            vec!["R201", "Alice"],
88        ]
89    );
90    assert_eq!(
91        exported_assignments[0],
92        BTreeMap::from([
93            ("room".to_string(), "R101"),
94            ("student".to_string(), "Alice"),
95        ])
96    );
97
98    println!("scheduled course completions: {:?}", scheduled.to_rows());
99
100    Ok(())
101}
Source

pub fn select<F>(&self, predicate: F) -> Self
where F: FnMut(&[T]) -> bool, T: Clone,

Returns the subset of rows that satisfy predicate.

The output keeps the same schema as self. Surviving rows remain in the deterministic order induced by the underlying BTreeSet.

§Examples
use relmath::NaryRelation;

let progress = NaryRelation::from_rows(
    ["student", "course", "status"],
    [
        ["Alice", "Math", "passed"],
        ["Alice", "Physics", "passed"],
        ["Bob", "Physics", "in_progress"],
    ],
)?;

let completed = progress.select(|row| row[2] == "passed");

assert_eq!(
    completed.to_rows(),
    vec![
        vec!["Alice", "Math", "passed"],
        vec!["Alice", "Physics", "passed"],
    ]
);
Examples found in repository?
examples/curriculum.rs (line 44)
7fn main() -> Result<(), relmath::NaryRelationError> {
8    let progress = NaryRelation::from_named_rows(
9        ["student", "course", "status"],
10        [
11            BTreeMap::from([
12                ("course", "Math"),
13                ("status", "passed"),
14                ("student", "Alice"),
15            ]),
16            BTreeMap::from([
17                ("course", "Physics"),
18                ("status", "passed"),
19                ("student", "Alice"),
20            ]),
21            BTreeMap::from([("course", "Math"), ("status", "passed"), ("student", "Bob")]),
22            BTreeMap::from([
23                ("course", "Physics"),
24                ("status", "in_progress"),
25                ("student", "Bob"),
26            ]),
27            BTreeMap::from([
28                ("course", "Math"),
29                ("status", "passed"),
30                ("student", "Cara"),
31            ]),
32        ],
33    )?;
34    let course_rooms = NaryRelation::from_named_rows(
35        ["course", "room"],
36        [
37            BTreeMap::from([("course", "Math"), ("room", "R101")]),
38            BTreeMap::from([("course", "Physics"), ("room", "R201")]),
39            BTreeMap::from([("course", "History"), ("room", "R301")]),
40        ],
41    )?;
42
43    let completed = progress
44        .select(|row| row[2] == "passed")
45        .project(["student", "course"])?;
46    let scheduled = completed.natural_join(&course_rooms);
47    let by_room = scheduled.group_by(["room"])?;
48    let room_r101 = by_room.group(&["R101"]).expect("expected R101 group");
49    let room_assignments = scheduled.project(["room", "student"])?;
50    let exported_assignments = room_assignments.to_named_rows();
51
52    assert!(completed.contains_row(&["Alice", "Math"]));
53    assert_eq!(
54        scheduled.schema(),
55        &[
56            "student".to_string(),
57            "course".to_string(),
58            "room".to_string(),
59        ]
60    );
61    assert_eq!(
62        scheduled.to_rows(),
63        vec![
64            vec!["Alice", "Math", "R101"],
65            vec!["Alice", "Physics", "R201"],
66            vec!["Bob", "Math", "R101"],
67            vec!["Cara", "Math", "R101"],
68        ]
69    );
70    assert_eq!(by_room.key_schema(), &["room".to_string()]);
71    assert_eq!(by_room.member_schema(), scheduled.schema());
72    assert_eq!(by_room.counts(), vec![(vec!["R101"], 3), (vec!["R201"], 1)]);
73    assert_eq!(
74        room_r101.to_rows(),
75        vec![
76            vec!["Alice", "Math", "R101"],
77            vec!["Bob", "Math", "R101"],
78            vec!["Cara", "Math", "R101"],
79        ]
80    );
81    assert_eq!(
82        room_assignments.to_rows(),
83        vec![
84            vec!["R101", "Alice"],
85            vec!["R101", "Bob"],
86            vec!["R101", "Cara"],
87            vec!["R201", "Alice"],
88        ]
89    );
90    assert_eq!(
91        exported_assignments[0],
92        BTreeMap::from([
93            ("room".to_string(), "R101"),
94            ("student".to_string(), "Alice"),
95        ])
96    );
97
98    println!("scheduled course completions: {:?}", scheduled.to_rows());
99
100    Ok(())
101}
Source

pub fn project<I, S>(&self, columns: I) -> Result<Self, NaryRelationError>
where I: IntoIterator<Item = S>, S: AsRef<str>, T: Clone,

Projects the relation onto the named columns in the requested order.

The output schema follows the exact order of columns. Duplicate column names, unknown columns, and empty projections are rejected by the current exact core.

§Examples
use relmath::NaryRelation;

let passed = NaryRelation::from_rows(
    ["student", "course", "status"],
    [
        ["Alice", "Math", "passed"],
        ["Cara", "Math", "passed"],
        ["Alice", "Physics", "passed"],
    ],
)?;

let projected = passed.project(["course", "student"])?;

assert_eq!(
    projected.schema(),
    &["course".to_string(), "student".to_string()]
);
assert_eq!(
    projected.to_rows(),
    vec![
        vec!["Math", "Alice"],
        vec!["Math", "Cara"],
        vec!["Physics", "Alice"],
    ]
);
Examples found in repository?
examples/curriculum.rs (line 45)
7fn main() -> Result<(), relmath::NaryRelationError> {
8    let progress = NaryRelation::from_named_rows(
9        ["student", "course", "status"],
10        [
11            BTreeMap::from([
12                ("course", "Math"),
13                ("status", "passed"),
14                ("student", "Alice"),
15            ]),
16            BTreeMap::from([
17                ("course", "Physics"),
18                ("status", "passed"),
19                ("student", "Alice"),
20            ]),
21            BTreeMap::from([("course", "Math"), ("status", "passed"), ("student", "Bob")]),
22            BTreeMap::from([
23                ("course", "Physics"),
24                ("status", "in_progress"),
25                ("student", "Bob"),
26            ]),
27            BTreeMap::from([
28                ("course", "Math"),
29                ("status", "passed"),
30                ("student", "Cara"),
31            ]),
32        ],
33    )?;
34    let course_rooms = NaryRelation::from_named_rows(
35        ["course", "room"],
36        [
37            BTreeMap::from([("course", "Math"), ("room", "R101")]),
38            BTreeMap::from([("course", "Physics"), ("room", "R201")]),
39            BTreeMap::from([("course", "History"), ("room", "R301")]),
40        ],
41    )?;
42
43    let completed = progress
44        .select(|row| row[2] == "passed")
45        .project(["student", "course"])?;
46    let scheduled = completed.natural_join(&course_rooms);
47    let by_room = scheduled.group_by(["room"])?;
48    let room_r101 = by_room.group(&["R101"]).expect("expected R101 group");
49    let room_assignments = scheduled.project(["room", "student"])?;
50    let exported_assignments = room_assignments.to_named_rows();
51
52    assert!(completed.contains_row(&["Alice", "Math"]));
53    assert_eq!(
54        scheduled.schema(),
55        &[
56            "student".to_string(),
57            "course".to_string(),
58            "room".to_string(),
59        ]
60    );
61    assert_eq!(
62        scheduled.to_rows(),
63        vec![
64            vec!["Alice", "Math", "R101"],
65            vec!["Alice", "Physics", "R201"],
66            vec!["Bob", "Math", "R101"],
67            vec!["Cara", "Math", "R101"],
68        ]
69    );
70    assert_eq!(by_room.key_schema(), &["room".to_string()]);
71    assert_eq!(by_room.member_schema(), scheduled.schema());
72    assert_eq!(by_room.counts(), vec![(vec!["R101"], 3), (vec!["R201"], 1)]);
73    assert_eq!(
74        room_r101.to_rows(),
75        vec![
76            vec!["Alice", "Math", "R101"],
77            vec!["Bob", "Math", "R101"],
78            vec!["Cara", "Math", "R101"],
79        ]
80    );
81    assert_eq!(
82        room_assignments.to_rows(),
83        vec![
84            vec!["R101", "Alice"],
85            vec!["R101", "Bob"],
86            vec!["R101", "Cara"],
87            vec!["R201", "Alice"],
88        ]
89    );
90    assert_eq!(
91        exported_assignments[0],
92        BTreeMap::from([
93            ("room".to_string(), "R101"),
94            ("student".to_string(), "Alice"),
95        ])
96    );
97
98    println!("scheduled course completions: {:?}", scheduled.to_rows());
99
100    Ok(())
101}
Source

pub fn rename<S>(&self, from: &str, to: S) -> Result<Self, NaryRelationError>
where S: Into<String>, T: Clone,

Returns a relation with one column renamed.

Renaming a column to its current name is a no-op. The new column name must be non-blank and must not duplicate another column in the schema.

§Examples
use relmath::NaryRelation;

let completed = NaryRelation::from_rows(
    ["student", "course"],
    [["Alice", "Math"], ["Bob", "Physics"]],
)?;

let renamed = completed.rename("student", "learner")?;

assert_eq!(
    renamed.schema(),
    &["learner".to_string(), "course".to_string()]
);
assert_eq!(
    renamed.to_rows(),
    vec![vec!["Alice", "Math"], vec!["Bob", "Physics"]]
);
Source

pub fn union(&self, other: &Self) -> Result<Self, NaryRelationError>
where T: Clone,

Returns the union of self and other when schemas match exactly.

Schema compatibility for the current exact core means exact schema equality, including column order.

§Examples
use relmath::NaryRelation;

let left = NaryRelation::from_rows(["student"], [["Alice"], ["Bob"]])?;
let right = NaryRelation::from_rows(["student"], [["Bob"], ["Cara"]])?;

let union = left.union(&right)?;

assert_eq!(
    union.to_rows(),
    vec![vec!["Alice"], vec!["Bob"], vec!["Cara"]]
);
Source

pub fn intersection(&self, other: &Self) -> Result<Self, NaryRelationError>
where T: Clone,

Returns the intersection of self and other when schemas match exactly.

Schema compatibility for the current exact core means exact schema equality, including column order.

§Examples
use relmath::NaryRelation;

let left = NaryRelation::from_rows(["student"], [["Alice"], ["Bob"]])?;
let right = NaryRelation::from_rows(["student"], [["Bob"], ["Cara"]])?;

let intersection = left.intersection(&right)?;

assert_eq!(intersection.to_rows(), vec![vec!["Bob"]]);
Source

pub fn difference(&self, other: &Self) -> Result<Self, NaryRelationError>
where T: Clone,

Returns the set difference self \ other when schemas match exactly.

Schema compatibility for the current exact core means exact schema equality, including column order.

§Examples
use relmath::NaryRelation;

let left = NaryRelation::from_rows(["student"], [["Alice"], ["Bob"]])?;
let right = NaryRelation::from_rows(["student"], [["Bob"], ["Cara"]])?;

let only_left = left.difference(&right)?;

assert_eq!(only_left.to_rows(), vec![vec!["Alice"]]);
Source

pub fn natural_join(&self, other: &Self) -> Self
where T: Clone,

Returns the natural join of self and other.

Shared column names constrain row matching: a pair of rows joins only when every shared column has equal values on both sides. When the schemas are disjoint, natural join behaves as a cartesian product.

The output schema keeps the entire left-hand schema in order, followed by the right-hand columns that are not shared, in their original order. Output rows are stored in a BTreeSet, so iteration remains deterministic. When no rows match, the result is empty but still keeps the joined schema.

§Examples
use relmath::NaryRelation;

let completed = NaryRelation::from_rows(
    ["student", "course"],
    [
        ["Alice", "Math"],
        ["Alice", "Physics"],
        ["Bob", "Math"],
    ],
)?;
let rooms = NaryRelation::from_rows(
    ["course", "room"],
    [["Math", "R101"], ["Physics", "R201"]],
)?;

let scheduled = completed.natural_join(&rooms);

assert_eq!(
    scheduled.schema(),
    &["student".to_string(), "course".to_string(), "room".to_string()]
);
assert_eq!(
    scheduled.to_rows(),
    vec![
        vec!["Alice", "Math", "R101"],
        vec!["Alice", "Physics", "R201"],
        vec!["Bob", "Math", "R101"],
    ]
);
Examples found in repository?
examples/curriculum.rs (line 46)
7fn main() -> Result<(), relmath::NaryRelationError> {
8    let progress = NaryRelation::from_named_rows(
9        ["student", "course", "status"],
10        [
11            BTreeMap::from([
12                ("course", "Math"),
13                ("status", "passed"),
14                ("student", "Alice"),
15            ]),
16            BTreeMap::from([
17                ("course", "Physics"),
18                ("status", "passed"),
19                ("student", "Alice"),
20            ]),
21            BTreeMap::from([("course", "Math"), ("status", "passed"), ("student", "Bob")]),
22            BTreeMap::from([
23                ("course", "Physics"),
24                ("status", "in_progress"),
25                ("student", "Bob"),
26            ]),
27            BTreeMap::from([
28                ("course", "Math"),
29                ("status", "passed"),
30                ("student", "Cara"),
31            ]),
32        ],
33    )?;
34    let course_rooms = NaryRelation::from_named_rows(
35        ["course", "room"],
36        [
37            BTreeMap::from([("course", "Math"), ("room", "R101")]),
38            BTreeMap::from([("course", "Physics"), ("room", "R201")]),
39            BTreeMap::from([("course", "History"), ("room", "R301")]),
40        ],
41    )?;
42
43    let completed = progress
44        .select(|row| row[2] == "passed")
45        .project(["student", "course"])?;
46    let scheduled = completed.natural_join(&course_rooms);
47    let by_room = scheduled.group_by(["room"])?;
48    let room_r101 = by_room.group(&["R101"]).expect("expected R101 group");
49    let room_assignments = scheduled.project(["room", "student"])?;
50    let exported_assignments = room_assignments.to_named_rows();
51
52    assert!(completed.contains_row(&["Alice", "Math"]));
53    assert_eq!(
54        scheduled.schema(),
55        &[
56            "student".to_string(),
57            "course".to_string(),
58            "room".to_string(),
59        ]
60    );
61    assert_eq!(
62        scheduled.to_rows(),
63        vec![
64            vec!["Alice", "Math", "R101"],
65            vec!["Alice", "Physics", "R201"],
66            vec!["Bob", "Math", "R101"],
67            vec!["Cara", "Math", "R101"],
68        ]
69    );
70    assert_eq!(by_room.key_schema(), &["room".to_string()]);
71    assert_eq!(by_room.member_schema(), scheduled.schema());
72    assert_eq!(by_room.counts(), vec![(vec!["R101"], 3), (vec!["R201"], 1)]);
73    assert_eq!(
74        room_r101.to_rows(),
75        vec![
76            vec!["Alice", "Math", "R101"],
77            vec!["Bob", "Math", "R101"],
78            vec!["Cara", "Math", "R101"],
79        ]
80    );
81    assert_eq!(
82        room_assignments.to_rows(),
83        vec![
84            vec!["R101", "Alice"],
85            vec!["R101", "Bob"],
86            vec!["R101", "Cara"],
87            vec!["R201", "Alice"],
88        ]
89    );
90    assert_eq!(
91        exported_assignments[0],
92        BTreeMap::from([
93            ("room".to_string(), "R101"),
94            ("student".to_string(), "Alice"),
95        ])
96    );
97
98    println!("scheduled course completions: {:?}", scheduled.to_rows());
99
100    Ok(())
101}

Trait Implementations§

Source§

impl<T: Clone + Ord> Clone for NaryRelation<T>

Source§

fn clone(&self) -> NaryRelation<T>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug + Ord> Debug for NaryRelation<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Ord> FiniteRelation for NaryRelation<T>

Source§

fn len(&self) -> usize

Returns the number of stored tuples in the relation.
Source§

fn is_empty(&self) -> bool

Returns true when the relation contains no tuples.
Source§

impl<T: PartialEq + Ord> PartialEq for NaryRelation<T>

Source§

fn eq(&self, other: &NaryRelation<T>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<T: Eq + Ord> Eq for NaryRelation<T>

Source§

impl<T: Ord> StructuralPartialEq for NaryRelation<T>

Auto Trait Implementations§

§

impl<T> Freeze for NaryRelation<T>

§

impl<T> RefUnwindSafe for NaryRelation<T>
where T: RefUnwindSafe,

§

impl<T> Send for NaryRelation<T>
where T: Send,

§

impl<T> Sync for NaryRelation<T>
where T: Sync,

§

impl<T> Unpin for NaryRelation<T>

§

impl<T> UnsafeUnpin for NaryRelation<T>

§

impl<T> UnwindSafe for NaryRelation<T>
where T: RefUnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.