Есть ли способ удалить развертывание элементов в цепочке итераторов, которые фильтруют и разбивают на основе результатов? - PullRequest
0 голосов
/ 09 июня 2018

Я создал этот пример кода:

fn main() {
    let books = vec![
        Book {
            data: Ok("type1".to_owned()),
            metadata: "meta1".to_owned(),
        },
        Book {
            data: Err("-".to_owned()),
            metadata: "meta2".to_owned(),
        },
        Book {
            data: Ok("type2".to_owned()),
            metadata: "meta2".to_owned(),
        },
    ];

    // metadata without data being error
    let (book_type_1, book_type_2): &(Vec<_>, Vec<_>) = &books
        .iter()
        .filter(|f| f.data.is_ok())
        .partition(|p| p.data.as_ref().unwrap() == "type1");

    println!("Books {:?}", books);
    println!("Type 1 {:?}", book_type_1); // Should be the original Vec<Book> with Type 1 filtered.
    println!("Type 2 {:?}", book_type_2); // Should be the original Vec<Book> with Type 2 filtered.
}

#[derive(Debug)]
struct Book {
    data: Result<String, String>,
    metadata: String,
}

В выражении let (book_type_1, book_type_2) мне нужно дважды использовать Book::data, но я уже отфильтровал его, поэтому знаю, что это не может быть Err,Есть ли способ реструктурировать, чтобы исключить использование unwrap здесь?

Ответы [ 2 ]

0 голосов
/ 09 июня 2018

Я бы использовал flat_map, как описано в другом ответе , но я оставил бы полученные значения как Result.Result реализует IntoIterator, поэтому вы можете использовать его непосредственно в flat_map.

Я бы также использовал Result::as_ref вместо выписыванияявно совпадать.

Затем я бы использовал Itertools::partition_map для одновременного выбора между типами и удаления дополнительного свойства:

extern crate itertools;
use itertools::{Either, Itertools};

// ...

let (book_type_1, book_type_2): (Vec<_>, Vec<_>) = books
    .iter()
    .flat_map(|b| b.data.as_ref().map(|data| (b, data)))
    .partition_map(|(b, data)| {
        if data == "type1" {
            Either::Left(b)
        } else {
            Either::Right(b)
        }
    });

Примечание:

  • Нет смысла ссылаться на кортеж результата.
  • Это работает только потому, что вы перебираете ссылки на книги.

Если бы вам нужно было работать с Book s, я бы переместил сравнение в вызов flat_map и передал бы его:

let (book_type_1, book_type_2): (Vec<_>, Vec<_>) = books
    .into_iter()
    .flat_map(|book| {
        book.data
            .as_ref()
            .ok()
            .map(|data| data == "type1")
            .map(|is_type1| (is_type1, book))
    })
    .partition_map(|(is_type1, book)| {
        if is_type1 {
            Either::Left(book)
        } else {
            Either::Right(book)
        }
    });

Также естьпрагматичное решение сортировки всех ошибок в одну и ту же корзину с одним из типов.Поскольку вы знаете, что не будет ошибок, это не будет иметь никакого значения:

let (book_type_1, book_type_2): (Vec<_>, Vec<_>) = books
    .iter()
    .filter(|f| f.data.is_ok())
    .partition(|p| p.data.as_ref().ok().map_or(false, |d| d == "type1"));

См. Также:

0 голосов
/ 09 июня 2018

Я не совсем уверен, что вы имеете в виду, но кажется, что вы хотите использовать Iterator::filter_map().Он позволяет вам фильтровать значения Some(T), которые затем передаются как развернутые T s.

Так что вы можете сделать, это преобразовать ваши Result s в Option s с Result::ok(), поэтому Result::Ok(T) станет Some(T), что означает, что он проходит фильтр как T.

fn main() {
    let books = vec![
        Book {
            data: Ok("type1".to_owned()),
            metadata: "meta1".to_owned(),
        },
        Book {
            data: Err("-".to_owned()),
            metadata: "meta2".to_owned(),
        },
        Book {
            data: Ok("type2".to_owned()),
            metadata: "meta2".to_owned(),
        },
    ];

    // metadata without data being error
    let (book_type_1, book_type_2): (Vec<_>, Vec<_>) = books
        .iter()
        .filter_map(|f| {
            match &f.data {
                Ok(data) => Some((f, data)),
                Err(_) => None,
            }
        })
        .partition(|(book, data)| *data == "type1");

    println!("Books {:?}", books);
    println!("Type 1 {:?}", book_type_1);
    println!("Type 2 {:?}", book_type_2);
}

#[derive(Debug)]
struct Book {
    data: Result<String, String>,
    metadata: String,
}

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

Я удалил ненужную ссылку на возвращенный секционированный кортеж.

Также обратите внимание, что None относится к Option<T>, но вы используете Result<T, E>.Я думаю, ты знал это, но просто убедился.

...