Как создать карту, которая может быть отсортирована по одному из значений? - PullRequest
2 голосов
/ 11 ноября 2019

Почему этот вопрос существует?

Я пишу rabbithole-rs , который представляет собой реализацию JSON: API, основанную на Rust. Но при разработке функций сортировки / разбивки на страницы, я сталкиваюсь с большой проблемой при разработке полей attribute.

Intro JSON: API soring / pagination кратко

Есть поля attributesв JSON: объект API resource, который является HashMap<String, serde_json::Value> или HashMap<String, Box<Any>>, где ключом является имя атрибута , а значением является значение атрибута.

И Vec<Resource> У объекта есть метод sort(attribute_name: &str), который может сортировать группу Resource на основе одного из атрибутов.

Краткое описание Resource

pub struct Resource {
    pub ty: String, // The type of `Resource`, `sort` method can only effect on the same type of resources
    pub id: String, // Every `Resource` has an unique id
    pub attr1: dyn Ord + Serialize + Deserialize,
    ... <more attrs>
}

Где:

  • Каждый attr должен реализовывать как минимум три черты:
    • Ord для сравнения
    • Serialize для преобразования в serde_json::Value
    • Deserialize для преобразования из serde_json::Value

В чем проблема?

При получении Resource мне нужно извлечь всеattribute полей и поместите их в HashMap, например: HashMap<String, serde_json::Value> или HashMap<String, Box<Any>>. Но оба они теряют информацию о характеристиках атрибутов, поэтому я не могу сравнить два элемента атрибута с одинаковыми именами.

Краткая демонстрация, пожалуйста!

Конечно! Вот, пожалуйста!

#[macro_use]
extern crate serde_derive;

use serde::{Deserialize, Serialize};
use std::any::Any;
use std::collections::HashMap;

/// This is a struct field
#[derive(Debug, Serialize, Deserialize, Ord, PartialOrd, PartialEq, Eq)]
pub struct Name {
    pub first_name: String,
    pub last_name: String,
}

/// Resource object
#[derive(Debug, Serialize, Deserialize, Ord, PartialOrd, PartialEq, Eq)]
pub struct Resource {
    pub ty: String,
    pub id: String,
    pub age: i32, // attr 1
    pub name: Name, // another attr
}

fn main() {
    let item1 = Resource {
        ty: "human".into(),
        id: "id1".to_string(),
        age: 1,
        name: Name {
            first_name: "first1".to_string(),
            last_name: "last1".to_string(),
        },
    };

    // **This is the first attributes HashMap**
    let mut map_item1: HashMap<&str, (&str, Box<dyn Any>)> = Default::default();
    map_item1.insert("age", ("i32", Box::new(item1.age)));
    map_item1.insert("name", ("Name", Box::new(item1.name)));

    let item2 = Resource {
        ty: "human".into(),
        id: "id2".to_string(),
        age: 2,
        name: Name {
            first_name: "first2".to_string(),
            last_name: "last2".to_string(),
        },
    };

    // **This is the second attributes HashMap**
    let mut map_item2: HashMap<&str, (&str, Box<dyn Any>)> = Default::default();
    map_item2.insert("age", ("i32", Box::new(item2.age)));
    map_item2.insert("name", ("Name", Box::new(item2.name)));

    // TODO: NEED TO BE IMPLEMENTED
    for k in map_item1.keys() {
        println!(
            "key: {key}, item1.{key} < item2.{key}? {res}",
            key = k,
            res = map_item1.get(k).unwrap().1.cmp(map_item2.get(k).unwrap().1)
        )
    }
}
...