rooted_topological_sort.rs

#
use std::collections::{HashMap, VecDeque};
#

This function prints out a topological sort of a company. This function works for any DAG where there is one root node which can have many children.

pub fn rooted_topological_sort(chart: &[(u32, u32, String)]) -> Option<Vec<(u64, String)>> {
    let mut emp_to_mgr: HashMap<u32, u32> = HashMap::new();
    let mut mgr_to_emp: HashMap<u32, Vec<u32>> = HashMap::new();
    let mut emp_to_name: HashMap<u32, String> = HashMap::new();
#

We start out without having found the CEO.

    let mut ceo = None;
#

For every employee in the chart

    for (emp_id, mgr_id, name) in chart {
#

If they manage themselves, they’re the CEO.

        if emp_id == mgr_id {
            ceo = Some(emp_id);
        } else {
#

Otherwise, we add the employee as a subordinate to their manager

            mgr_to_emp.entry(*mgr_id).or_default().push(*emp_id);
#

And add their manager to the employee.

            emp_to_mgr.insert(*emp_id, *mgr_id);
        }
#

We then maintain a mapping of employee to name for faster lookup.

        emp_to_name.insert(*emp_id, name.to_string());
    }
#

If we can’t find a CEO, then there’s no way to continue

    ceo?;
#

Next, we bfs through the org chart, starting with the CEO

    let mut q = VecDeque::new();
    q.push_back((ceo.unwrap(), 0));

    let mut res = vec![];
#

While we have employees to process

    while let Some((emp_id, depth)) = q.pop_front() {
#

we push the employee and its depth to the result

        res.push((depth, emp_to_name[emp_id].clone()));
#

then, for this employee’s subordinates (if there are any)

        if let Some(reports) = mgr_to_emp.get(emp_id) {
#

We add them to the front of the queue, since we want to process them before other peer employees.

            for report_id in reports {
                q.push_front((report_id, depth + 1));
            }
        }
    }
#

And then we return the collection.

    Some(res)
}

#[cfg(test)]
mod tests {
    use super::*;
    use insta::assert_yaml_snapshot;
    use quickcheck_macros::quickcheck;

    #[quickcheck]
    fn verify(chart: Vec<(u32, u32, String)>) -> bool {
        if chart.len() > 100 {
            return true;
        }
        rooted_topological_sort(&chart);
        true
    }

    #[test]
    fn example() {
        let chart = vec![
            (6, 5, "acolyte1".to_string()),
            (7, 4, "acolyte2".to_string()),
            (2, 1, "sylvanas".to_string()),
            (3, 1, "anubarak".to_string()),
            (4, 2, "commander1".to_string()),
            (1, 1, "arthas".to_string()),
            (5, 3, "commander2".to_string()),
        ];

        let result = rooted_topological_sort(&chart);
        assert_yaml_snapshot!(result);
    }
}