Hi all, I've created a custom struct that is of this shape:
struct User{
surname: String,
name: String,
interests: HashMap<String, String>,
}
now I want to define an order for my struct in a way that I can pull in a TreeSet for instance.
My ordering rule is: surname first, if they are the same then order by name.
It this is also the same then use the interest map in this way:
- compare keys first,
- if keys are the same, compare the values
- do this until you consume all entries from left and right. When you are done, if you found all equals, then return left.size - right.size.
in practice, this is an implementation in Kotlin of the above interests:
private val interestsComparator =
Comparator<Set<Map.Entry<String, String>>> { first, second ->
first.asSequence()
.zip(second.asSequence())
.map { (first, second) -> entryComparator.compare(first, second) }
.filter { it != 0 }
.firstOrNull()
?: (first.size - second.size)
}
private val entryComparator: Comparator<Map.Entry<String, String>> = compareBy(
{ it.key },
{ it.value }
)
I've tried deriving partEq and friends but of course the hashmap comparison is not defined.. So I was trying myself but the code that I came up with starts to bee too verbose (I should add ifs, elses and such) so I wonder if I can use a more modern implementation like the one above..
On a side note, I've spent some time reading why I needed to implement Ord, PartEq, PartOrd, Eq.. that really seems overkill to me when I can just define a comparator.. do you have any link that you can point me to study why's that in the rust world? I've been more confused after reading the official doc
For completeness, I'm attaching my file here. Maybe you can also give some suggestions on the tests (I've tried to use string constants but it's a no-no from rust compiler
#[derive(Eq, Debug)]
pub struct User {
name: String,
surname: String,
interests: HashMap<String, String>,
}
impl User {
pub fn create(
name: String,
surname: String,
interests: HashMap<String, String>,
) -> User {
User {
name,
surname,
interests,
}
}
}
impl Ord for User {
fn cmp(&self, other: &Self) -> Ordering {
self.name
.cmp(&other.name)
.then(self.surname.cmp((&other.surname)))
//now what?
}
}
impl PartialOrd for User {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other)) // why I need to do this again?
}
}
impl PartialEq for User {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.surname == other.surname
&& self.interests == other.interests
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn it_orders_with_name_first() {
let first_user = User::create(
String::from("name0"),
String::from("surname1"),
HashMap::new(),
);
let second_user = User::create(
String::from("name1"),
String::from("surname0"),
HashMap::new(),
);
let mut users = BTreeSet::new();
users.insert(&second_user);
users.insert(&first_user);
let first = users.into_iter().next().unwrap();
assert_eq!(&first_user, first)
}
#[test]
fn it_orders_with_same_name_but_different_surname() {
let first_user = User::create(
String::from("name0"),
String::from("surname0"),
HashMap::new(),
);
let second_user = User::create(
String::from("name0"),
String::from("surname1"),
HashMap::new(),
);
let mut users = BTreeSet::new();
users.insert(&second_user);
users.insert(&first_user);
let first = users.into_iter().next().unwrap();
assert_eq!(&first_user, first)
}
}