我创建了这个示例代码:
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
在这里的使用?
答案 0 :(得分:2)
我不确定你的意思,但似乎你想使用Iterator::filter_map()
。它允许您过滤Some(T)
的值,然后将其作为未打包的T
传递。
所以你可以做的是将你的Result
转换为Option
,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>
相关,但您使用public function findByTerms($terms) : array
{
$alias = "d";
$qb = $this->createQueryBuilder($alias);
foreach (explode(" ", $terms) as $i => $term) {
$qb
->andWhere($qb->expr()->orX( // nested condition
$qb->expr()->like($alias . ".name", ":term" . $i),
$qb->expr()->like($alias . ".description", ":term" . $i)
))
->setParameter("term" . $i, "%" . $term . "%")
;
}
return $qb->getQuery()->getResult();
}
。我想你知道这一点,但只是确定。
答案 1 :(得分:1)
我会按in the other answer所述使用flat_map
,但我会将产生的值保留为Result
。 Result
implements 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
进行操作,我会将比较转移到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)
}
});
还有一种实用的解决方案,即将所有错误排序到与其中一种类型相同的bin中。既然你知道赢了是任何错误,这都没有效果:
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"));
另见: