luthfianto
luthfianto

Reputation: 1826

Create a Vec<BTreeSet<char>> from Vec<Vec<char>>

I'm trying to create a vector of set (Vec<BTreeSet<char>>) from Vec<Vec<char>>. Here's my progress so far:

use std::collections::BTreeSet;

fn main() {
    // The data
    let transaction_list = [
        vec!['A','B','C','D'],
        vec!['B','B','C'],
        vec!['A','B','B','D']
    ];

    // Successfully created a set from the Vec of Vec. It contains unique chars
    let item_set: BTreeSet<char> = transaction_list.iter().flat_map(|t| t).cloned().collect();

    // Made the same Vec of Vec. Basically just experimenting with map and collect
    let the_same_transaction_list: Vec<Vec<char>> = transaction_list.iter().map(|t| t ).cloned().collect::<Vec<_>>();

    // ERROR
    let transaction_set: Vec<BTreeSet<char>> = transaction_list
                                                .iter()
                                                .map(|t| t.iter().map(|t| t).cloned().collect() )
                                                .cloned().collect::<Vec<_>>();
}

The error message was:

error: the trait `core::iter::FromIterator<char>` is not implemented for the type `&_` [E0277]
                                      .map(|t| t.iter().map(|t| t).cloned().collect() )
                                                                            ^~~~~~~~~
help: see the detailed explanation for E0277
note: a collection of type `&_` cannot be built from an iterator over elements of type `char`

I haven't found the correct way to make a Vec<BTreeSet<char>> out of Vec<Vec<char>>. Here's the playground url: http://is.gd/WVONHY.

Upvotes: 5

Views: 2536

Answers (1)

Shepmaster
Shepmaster

Reputation: 431809

The error message is a bit odd. Here's the solution:

let transaction_set: Vec<BTreeSet<_>> =
    transaction_list
    .iter()
    .map(|t| t.iter().cloned().collect())
    .collect();

The two main changes are:

  1. map(|x| x) is pointless. It is a no-op.
  2. I removed the outer cloned. The result type of the map call is already going to be a BTreeSet. There's no need to then clone it again.

The latter fixes your problem, so let's look at the definition:

fn cloned<'a, T>(self) -> Cloned<Self> 
    where Self: Iterator<Item=&'a T>,
          T: 'a + Clone

To be able to call cloned, the iterator Item has to be a reference to something that is cloneable. However, you are trying to iterate over BTreeSets, which are not references. The compiler chooses to tell you that there's no way to collect your inner iterator of char into a &_ (a reference to some type), which would then satisfy the requirement to call cloned. My guess is that the inner type has more wiggle-room than the type needed by collect. If we rewrite the original code a bit to have a more definitive type on the inside:

let transaction_set: Vec<_> =
    transaction_list
    .iter()
    .map(|t| -> BTreeSet<_> {t.iter().cloned().collect()})
    .cloned().collect();

We get a different set of errors:

error: type mismatch resolving `<[closure...] as core::ops::FnOnce<(&collections::vec::Vec<char>,)>>::Output == &_`:
 expected struct `collections::btree::set::BTreeSet`,
    found &-ptr [E0271]
         .cloned().collect();
          ^~~~~~~~

error: no method named `collect` found for type `core::iter::Cloned<core::iter::Map<core::slice::Iter<'_, collections::vec::Vec<char>>, [closure...]>>` in the current scope
         .cloned().collect();
                   ^~~~~~~~~
note: the method `collect` exists but the following trait bounds were not satisfied: `core::iter::Cloned<core::iter::Map<core::slice::Iter<'_, collections::vec::Vec<char>>, [closure...]>> : core::iter::Iterator`

Which helps highlight that the problem arises from the outer use of cloned.

Upvotes: 4

Related Questions