【问题标题】:How to map or transform a vector of structs into another vector of structs?如何将一个结构向量映射或转换为另一个结构向量?
【发布时间】:2020-06-30 23:55:13
【问题描述】:

最小可重现示例

pub struct User {
    pub id: i32,
    pub name: String,
    pub match_id: i32,
}

pub struct Match {
    pub id: i32,
    pub name: String,
}

pub struct MatchWithUsers {
    pub id: i32,
    pub name: String,
    pub users: Vec<User>,
}

fn main() {
    let query_result: Vec<(Match, Option<User>)> = vec![
        (
            Match {
                id: 1,
                name: String::from("1st match"),
            },
            Some(User {
                id: 1,
                name: String::from("Jack"),
                match_id: 1,
            }),
        ),
        (
            Match {
                id: 2,
                name: String::from("2nd match"),
            },
            Some(User {
                id: 2,
                name: String::from("John"),
                match_id: 2,
            }),
        ),
        (
            Match {
                id: 3,
                name: String::from("3rd match"),
            },
            None,
        ),
    ];
    let mut response: Vec<MatchWithUsers> = Vec::new();

    for (m, u) in &query_result {
        let existing_match = &response
            .into_iter()
            .find(|match_with_user| match_with_user.id == m.id);

        match existing_match {
            Some(found_match) => {
                println!("Inser user into match: {}", found_match.name);
                match u {
                    Some(mut user) => {
                        found_match.users.push(user);
                    }
                    None => println!("No users."),
                }
            }
            None => {
                println!("No existing match. Add to response.");
                let user = u.as_ref().unwrap();
                response.push(MatchWithUsers {
                    id: m.id,
                    name: m.name.clone(),
                    users: vec![],
                });
            }
        }
    }

    println!("Response with: {}", response.len());
}
warning: unused variable: `user`
  --> src/main.rs:69:21
   |
69 |                 let user = u.as_ref().unwrap();
   |                     ^^^^ help: consider prefixing with an underscore: `_user`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: variable does not need to be mutable
  --> src/main.rs:61:26
   |
61 |                     Some(mut user) => {
   |                          ----^^^^
   |                          |
   |                          help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

error[E0382]: use of moved value: `response`
  --> src/main.rs:53:31
   |
50 |     let mut response: Vec<MatchWithUsers> = Vec::new();
   |         ------------ move occurs because `response` has type `std::vec::Vec<MatchWithUsers>`, which does not implement the `Copy` trait
...
53 |         let existing_match = &response
   |                               ^^^^^^^^ value moved here, in previous iteration of loop

error[E0507]: cannot move out of `u.0` which is behind a shared reference
  --> src/main.rs:60:23
   |
60 |                 match u {
   |                       ^
61 |                     Some(mut user) => {
   |                          --------
   |                          |
   |                          data moved here
   |                          move occurs because `user` has type `User`, which does not implement the `Copy` trait

error[E0596]: cannot borrow `found_match.users` as mutable, as it is behind a `&` reference
  --> src/main.rs:62:25
   |
62 |                         found_match.users.push(user);
   |                         ^^^^^^^^^^^^^^^^^ `found_match` is a `&` reference, so the data it refers to cannot be borrowed as mutable

Playground

我的问题

我有an API test project using Rocket and Diesel

以下方法执行 Diesel 查询并将结果映射到 JSON 响应。这是数据库中所有与他们的用户匹配的响应。用户应该嵌套在每个Match 节点中。

我的解决方案尝试

  1. 我创建了一个向量。
  2. 在查询结果中迭代;
  3. 检查匹配是否已经存在于我的向量中,如果存在,添加用户信息(来自查询结果)并将其添加到当前MatchWithUser中的用户属性中,否则只需添加一个结构(@987654335 @) 到向量。
pub fn show_all_matches2() -> Vec<MatchWithUsers> {
    use schema::*;

    let connection = establish_connection();

    let query_result: Vec<(Match, Option<User>)> = matches::table
        .left_join(users::table.on(users::match_id.eq(matches::id)))
        .load(&connection)
        .expect("Error loading matches");

    let mut response: Vec<MatchWithUsers> = Vec::new();

    for (m, u) in &query_result {
        let existing_match = &response
            .into_iter()
            .find(|match_with_user| match_with_user.id == m.id);

        match existing_match {
            Some(mut found_match) => {
                println!("Inser user into match: {}", found_match.name);
                found_match.users.push(u.unwrap());
            }
            None => {
                println!("No existing match. Add to response.");
                let user = u.as_ref().unwrap();
                response.push(MatchWithUsers {
                    id: m.id,
                    name: m.name.clone(),
                    players_count: m.players_count,
                    users: vec![User {
                        id: user.id,
                        name: user.name.clone(),
                        match_id: user.match_id,
                    }],
                });
            }
        }
    }

    response
}

结构

use crate::schema::{matches, users};
use serde::{Deserialize, Serialize};

#[derive(Queryable, Identifiable, Associations, Serialize, Deserialize)]
#[belongs_to(Match)]
#[table_name = "users"]
pub struct User {
    pub id: i32,
    pub name: String,
    pub match_id: i32,
}

#[derive(Queryable, Identifiable, Serialize, Deserialize)]
#[table_name = "matches"]
pub struct Match {
    pub id: i32,
    pub name: String,
    pub players_count: i32,
}

#[derive(Serialize, Deserialize)]
pub struct MatchWithUsers {
    pub id: i32,
    pub name: String,
    pub players_count: i32,
    pub users: Vec<User>,
}

错误

$ cargo run
   Compiling got_board_api_v3 v0.1.0 (/Users/tauil/Projects/got/got-board-api-v3)
error[E0382]: use of moved value: `response`
  --> src/lib.rs:54:31
   |
51 |     let mut response: Vec<MatchWithUsers> = Vec::new();
   |         ------------ move occurs because `response` has type `std::vec::Vec<models::MatchWithUsers>`, which does not implement the `Copy` trait
...
54 |         let existing_match = &response
   |                               ^^^^^^^^ value moved here, in previous iteration of loop

error[E0507]: cannot move out of `existing_match.0` which is behind a shared reference
  --> src/lib.rs:58:15
   |
58 |         match existing_match {
   |               ^^^^^^^^^^^^^^
59 |             Some(mut found_match) => {
   |                  ---------------
   |                  |
   |                  data moved here
   |                  move occurs because `found_match` has type `models::MatchWithUsers`, which does not implement the `Copy` trait

error[E0507]: cannot move out of `*u` which is behind a shared reference
  --> src/lib.rs:61:40
   |
61 |                 found_match.users.push(u.unwrap());
   |                                        ^
   |                                        |
   |                                        move occurs because `*u` has type `std::option::Option<models::User>`, which does not implement the `Copy` trait
   |                                        help: consider borrowing the `Option`'s content: `u.as_ref()`

Postgres 客户端查询结果:

select * from matches left join users on matches.id = users.match_id;

 id |        name         | players_count | id |   name    | match_id
----+---------------------+---------------+----+-----------+----------
  1 | My first match      |             3 |  1 | Rafael    |        1
  1 | My first match      |             3 |  2 | Leandro   |        1
  1 | My first match      |             3 |  3 | Vagner    |        1
  2 | Just a second match |             4 |  4 | Vagner    |        2
  2 | Just a second match |             4 |  5 | Leandro   |        2
  2 | Just a second match |             4 |  6 | Rafael    |        2
  2 | Just a second match |             4 |  7 | Wanderson |        2
  3 | Amazing match       |             6 |    |           |
(8 rows)

【问题讨论】:

  • 您应该考虑使用HashMap&lt;i32, MatchWithUsers&gt; 而不是Vec,这在查找重复项时效率更高,然后使用Entry API 插入或修改存储的值。
  • @Jmb 感谢您的反馈和回答。我已经用错误文本更新了问题。我也会尝试一下HashMap。我已经看到了一些例子,但我实际上与我最初想做的有点不同。
  • 看来Does Rust have a way to apply a function/method to each element in an array or vector? 的答案可能会回答您的问题。如果没有,请edit您的问题解释差异。否则,我们可以将此问题标记为已回答。
  • 很难回答您的问题,因为它不包含minimal reproducible example,强调minimal。如果您尝试在Rust Playground 上重现您的错误,如果可能的话,这将使我们更容易为您提供帮助,否则在全新的 Cargo 项目中,然后在edit 您的问题中包含附加信息。您可以使用Rust-specific MRE tips 来减少您在此处发布的原始代码。谢谢!
  • 请将错误粘贴为问题中的文本,而不是图像。这样以后遇到同样问题的其他人就可以搜索到它。

标签: struct rust


【解决方案1】:

如果您使用 itertools crate 中的 group_by 并且确保查询结果按匹配 ID 排序,则可以非常轻松地获得与每个匹配项关联的用户的匹配项列表:

use itertools::Itertools; // 0.9.0
let response: Vec<_> = query_result
    .into_iter()
    // Note that this assumes that `query_result` is sorted
    // by match id since `group_by` only considers
    // consecutive matches.
    .group_by(|(m, _)| m.id)
    .into_iter()
    .map(|(id, mut g)| {
        // Now `g` is an iterator of `(Match, Option<User>)`
        // where all the matches are the same. We take the
        // first item to get the match information. Note
        // that it is safe to unwrap here because `group_by`
        // would never call us with an empty `g`.
        let (m, u) = g.next().unwrap();
        MatchWithUsers {
            id: id,
            name: m.name,
            // We got the first user along with the match
            // information, now we append the other users
            // from the remaining items in `g`.
            users: u
                .into_iter()
                .chain(g.flat_map(|(_, u)| u.into_iter()))
                .collect(),
        }
    })
    .collect();

Playground

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-09
    • 1970-01-01
    • 2011-04-08
    • 2023-03-16
    相关资源
    最近更新 更多