Как отобразить или преобразовать вектор структур в другой вектор структур? - PullRequest
0 голосов
/ 19 марта 2020

Минимальный воспроизводимый пример

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

Детская площадка

Моя проблема

У меня тестовый проект API с использованием Rocket и Diesel .

Следующий метод выполняет запрос Diesel и должен отобразить результат в ответ JSON. Это ответ всех совпадений в базе данных с их пользователями. Пользователи должны быть вложены в каждый узел Match.

Моя попытка решения

  1. Я создаю вектор.
  2. Повторяем в результате запроса;
  3. Проверьте, существует ли совпадение в моем векторе если это так, добавьте информацию о пользователе (исходя из результата запроса) и добавьте ее к атрибуту users в текущем MatchWithUser, в противном случае просто добавьте структуру (MatchWithUsers) к вектору.
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>,
}

Ошибки

errors when compiling

$ 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)

1 Ответ

0 голосов
/ 25 марта 2020

Вы можете очень легко получить список совпадений с пользователями, связанными с каждым совпадением, если вы используете group_by из ящика itertools и если вы убедитесь, что результаты вашего запроса отсортированы по идентификатору совпадения :

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();

Детская площадка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...