Search code examples
linqrusttraitsrust-cargorust-diesel

What is C# Linq "join" equivalent in rust?


I am trying to perform inner join on two vectors in rust but not sure how to achieve this.
In short, I am looking for employees with their department name.

If anybody is familiar with C# LINQ here, then I am trying to achieve something like below in rust.
Considering same model as specified below in model.rs

from e in employees
join d in department
on e.dept_id equals d.dept_id
select new 
{
    DeptName= d.dept_name,
    Name = e.name
};

Here is my rust language code:

models.rs

use bigdecimal::BigDecimal;
use chrono::{NaiveDateTime, Utc};

pub struct Department {
    pub dept_id: i32,
    pub dept_name: Option<String>,
    pub created_on: Option<NaiveDateTime>,
    pub created_by: Option<String>,
    pub modified_on: Option<NaiveDateTime>,
    pub modified_by: Option<String>,
    pub is_active: Option<bool>,
}

impl Department {
    pub fn get_departments() -> Vec<Department> {
        vec![
            Department {
                dept_id: 101,
                dept_name: Option::from("Software".to_string()),
                created_on: Option::from(Utc::now().naive_utc()),
                created_by: Option::from("Jadon".to_string()),
                modified_on: None,
                modified_by: None,
                is_active: Option::from(true),
            },
            Department {
                dept_id: 102,
                dept_name: Option::from("Hr".to_string()),
                created_on: Option::from(Utc::now().naive_utc()),
                created_by: Option::from("Jadon".to_string()),
                modified_on: None,
                modified_by: None,
                is_active: Option::from(true),
            },
            Department {
                dept_id: 103,
                dept_name: Option::from("Hr".to_string()),
                created_on: Option::from(Utc::now().naive_utc()),
                created_by: Option::from("Jadon".to_string()),
                modified_on: None,
                modified_by: None,
                is_active: Option::from(true),
            },
        ]
    }
}

pub struct Employee {
    pub employee_id: i32,
    pub name: Option<String>,
    pub age: Option<i32>,
    pub address: Option<String>,
    pub email: Option<String>,
    pub dept_id: i32,
    pub salary: Option<BigDecimal>,
    pub created_on: Option<NaiveDateTime>,
    pub created_by: Option<String>,
    pub modified_on: Option<NaiveDateTime>,
    pub modified_by: Option<String>,
    pub is_active: Option<bool>,
}

impl Employee {
    pub fn get_employees() -> Vec<Employee> {
        vec![
            Employee {
                employee_id: 1001,
                name: Option::from("Marten Babel".to_string()),
                age: Option::from(25),
                address: Option::from("Netherland".to_string()),
                email: Option::from("somemail@gmail.com".to_string()),
                dept_id: 101,
                salary: Option::from(BigDecimal::from(50000.00)),
                created_on: Option::from(Utc::now().naive_utc()),
                created_by: Option::from("Tom".to_string()),
                modified_on: None,
                modified_by: None,
                is_active: Option::from(true),
            },
            Employee {
                employee_id: 1002,
                name: Option::from("Jack Sancho".to_string()),
                age: Option::from(22),
                address: Option::from("England".to_string()),
                email: Option::from("jacksemail@gmail.com".to_string()),
                dept_id: 102,
                salary: Option::from(BigDecimal::from(80000.00)),
                created_on: Option::from(Utc::now().naive_utc()),
                created_by: Option::from("Tom".to_string()),
                modified_on: None,
                modified_by: None,
                is_active: Option::from(true),
            },
            Employee {
                employee_id: 1003,
                name: Option::from("Phil Foden".to_string()),
                age: Option::from(29),
                address: Option::from("England".to_string()),
                email: Option::from("philsemail@gmail.com".to_string()),
                dept_id: 101,
                salary: Option::from(BigDecimal::from(80000.00)),
                created_on: Option::from(Utc::now().naive_utc()),
                created_by: Option::from("Tom".to_string()),
                modified_on: None,
                modified_by: None,
                is_active: Option::from(true),
            },
        ]
    }
}

main.rs

fn main() {
    let department: Vec<Department> = Department::get_departments();
    for dept in department {
        println!(
            "Dept Id: {} and Dept Name: {}",
            dept.dept_id,
            dept.dept_name.unwrap_or_default()
        );
    }

    let employee: Vec<Employee> = Employee::get_employees();
    for emp in employee {
        println!(
            "Name is: {} and age is : {}",
            emp.name.unwrap_or_default(),
            emp.age.unwrap_or_default()
        )
    }
}

Here "dept_id" will be acting as a foreign key.
The code in main.rs is working fine. I am getting departments and employees but how can I perform join on these two vectors.

Note: As of now I am not using diesel orm. Only looking to perform join on these vectors.
Is it possible in rust ?
Thanks


Solution

  • If you just want to get pairs of &str for the names of the departments and the employees, you can use an iterator chain like so:

    let department: Vec<Department> = Department::get_departments();
    let employee: Vec<Employee> = Employee::get_employees();
    
    let dept_employee_names = department.iter().flat_map(|d| {
        let dept_id = d.dept_id;
        let dept_name = &d.dept_name;
        employee
            .iter()
            .filter(move |e| e.dept_id == dept_id)
            .map(move |e| {
                (
                    dept_name.as_deref().unwrap_or_default(),
                    e.name.as_deref().unwrap_or_default(),
                )
            })
    });
    
    for (dept, emp) in dept_employee_names {
        println!("dept = {}, emp = {}", dept, emp);
    }
    

    Note that this has to search through all of the employees for each department, but that is really a limitation of using vectors. You can probably make this more efficient by changing the data structures.