[SOLVED] (no) Problem mapping charaters of two strings in a hashmap


#1

Hello, I am kind of new to rust (i dove in a while back but not deep, and am trying again)

Here is a problem I encountered:

use std::collections::HashMap;

    struct Translation
    {
        search: String,
        replace: String
    }
    impl Translation {
        fn make_map(&mut self) -> HashMap<char, char> {
            let count = self.search.chars().count();
            let results = self.replace.chars().count();
            println!("keys: {}", count);
            println!("vals: {}", results);
            let mut keys = self.search.chars();
            for key in keys {
                println!("key: {:?}", key);
            }
            let mut values = self.replace.chars();
            for val in values {
                println!("val: {:?}", val);
            }
            keys = self.search.chars();
            values = self.replace.chars();
            println!("search: {}", self.search);
            println!("replace: {}", self.replace);
            let mut mapping = HashMap::new();
            for index in 0..count {
                let key = keys.nth(index);
                let val = values.nth(index);
                println!("index: {} key: {:?} val: {:?}", index, key, val);
                if key.is_some() && val.is_some() {
                    mapping.insert(key.unwrap(), val.unwrap());
                }
            }
            return mapping;
        }
    }

    fn main() {
        let mut tr = Translation {search: "abcde".to_string() , replace: "vwxyz".to_string()};
        let map = tr.make_map();
        for (key, val) in map.iter() {
            println!("key: {} val: {}", key, val);
        }
    }

this gives the following output:

$ rustc map.rs ; ./map
keys: 5
vals: 5
key: 'a'
key: 'b'
key: 'c'
key: 'd'
key: 'e'
val: 'v'
val: 'w'
val: 'x'
val: 'y'
val: 'z'
search: abcde
replace: vwxyz
index: 0 key: Some('a') val: Some('v')
index: 1 key: Some('c') val: Some('x')
index: 2 key: None val: None
index: 3 key: None val: None
index: 4 key: None val: None
key: a val: v
key: c val: x

in the mapping of the strings I seem to lose most of my characters. What is wrong with my tactic?


#2

please ignore. I overlooked the part where nth() consumes characters :blush:


#3

When you call nth on an iterator, it consumes the iterator. So if you call nth(0) on an iterator and then subsequently nth(1) on the same iterator, the second call will actually retrieve the third element of the iterator since the first had already been consumed.

Working version: https://is.gd/Ei98RC


#4

thanks @BurntSushi, you just beat me to it. I had replaced the call to nth() with a more ordinary next(), feeling very stupid


#5

@dahn Don’t feel stupid! Stuff can be hard. There are other cool ways to write your code though that you might be interested in. For example, instead of iterating over an index of characters, you could just iterate over the characters themselves:

let mut mapping = HashMap::new();
for (key, val) in self.search.chars().zip(self.replace.chars()) {
    println!("key: {:?} val: {:?}", key, val);
    mapping.insert(key, val);
}
return mapping;

Once you get there though, you can take it yet another step!

self.search.chars().zip(self.replace.chars()).collect()

This works because the return type of your function is HashMap<char, char> and HashMap implements the FromIterator trait. Those two things combined allow collect to build the hashmap itself.


#6

wow, sounds great. looks a bit like the streaming API from java8. I will use this and look into the FromIterator trait.

so much to learn, so much fun