luvguru69
luvguru69

Reputation: 55

How to Filter out a Vec of Strings From a Vec of Structs Rust

I have a working solution to filtering out an input vec of strings compared to a vector of a struct. However, my code seems complicated and I tried simplify the code using a iter::filter(https://doc.rust-lang.org/stable/std/iter/struct.Filter.html). This caused issues because the iterator gave back values that were references and could not be directly used. It seems like my understanding of the iter and what can be done in a structs vector needs refreshing. Below is the simplified filtering code that works:

#[derive(Debug)]
pub struct Widget {
    name: String,
    pin: u16,
}

impl Widget{
    pub fn new(widget_name: String, widget_pin: String) -> Widget {
        let widget_pin_u16 = widget_pin.parse::<u16>().expect("Unable to parse");
        let nw = Widget {
            name: widget_name,
            pin: widget_pin_u16
        };
        return nw
    }
}

pub struct WidgetHolder {
    widgets: Vec<Widget>,
    widget_holder_name: String
}

impl WidgetHolder {
    fn add_widgets(&mut self, valid_widgets_found: Vec<String>) {
        let mut widgets_to_add: Vec<String> = Vec::new();
        for widget in valid_widgets_found {
            // The string musy be compared to pin field, so we're converting
            let widget_offset = widget
                .clone()
                .parse::<u16>()
                .expect("Unable to parse widget base into int.");
            // If it doesnt exist in our widgetHolder widgets vector, then lets add it.
            let mut widget_exists = false;
            for existing_widget in &self.widgets {
                if widget_offset == existing_widget.pin {
                    widget_exists = true;
                    break;
                }
            }
            if !widget_exists {
                widgets_to_add.push(widget.clone());
            }
        }
        
        if widgets_to_add.is_empty() {
            return;
        }
        
        for widget in widgets_to_add {
            let loaded_widget = Widget::new(self.widget_holder_name.clone(), widget);
            self.widgets.push(loaded_widget);
        }
    }
}


pub fn main() {
    let init_vec = Vec::new();
    let mut wh = WidgetHolder {
        widgets: init_vec,
        widget_holder_name: "MyWidget".to_string()
    };
    let vec1 = vec!["1".to_string(), "2".to_string(), "3".to_string()];
    wh.add_widgets(vec1);
    println!("{:?}", wh.widgets);

    let vec2 = vec!["2".to_string(), "3".to_string(), "4".to_string()];
    wh.add_widgets(vec2);
    println!("{:?}", wh.widgets);
}

Is there a way I can clean up this code without having to use so many data structures and loops? The filter api looks clean but does it work with a vector inside of a struct that I am trying to mutate(append to it)?

EDIT After trying to get a stack trace, I actually got the filter to work...


    fn add_widgets(&mut self, valid_widgets_found: Vec<String>) {
        let widgets_to_add: Vec<String> = valid_widgets_found.into_iter()
                .filter(|widget_pin| {
                    let widget_offset = widget_pin.clone().parse::<u16>().expect("Unable to parse widget base into int.");
                    let mut widget_exists = false;
                    for existing_widget in &self.widgets {
                        if widget_offset == existing_widget.pin {
                            widget_exists = true;
                            break;
                        }
                    }
                    !widget_exists
                })
                .collect();
        
        if widgets_to_add.is_empty() {
            return;
        }
        
        for widget in widgets_to_add {
            let loaded_widget = Widget::new(self.widget_holder_name.clone(), widget);
            self.widgets.push(loaded_widget);
        }
    }

Upvotes: 1

Views: 525

Answers (1)

luvguru69
luvguru69

Reputation: 55

I figured out the answer. Seemed like a syntax error when I initially tried it. For anyone who's looking for a filter example in the future:


    fn add_widgets(&mut self, valid_widgets_found: Vec<String>) {
        let widgets_to_add: Vec<String> = valid_widgets_found.into_iter()
                .filter(|widget_pin| {
                    let widget_offset = widget_pin.clone().parse::<u16>().expect("Unable to parse widget base into int.");
                    let mut widget_exists = false;
                    for existing_widget in &self.widgets {
                        if widget_offset == existing_widget.pin {
                            widget_exists = true;
                            break;
                        }
                    }
                    !widget_exists
                })
                .collect();
        
        if widgets_to_add.is_empty() {
            return;
        }
        
        for widget in widgets_to_add {
            let loaded_widget = Widget::new(self.widget_holder_name.clone(), widget);
            self.widgets.push(loaded_widget);
        }
    }

Upvotes: 1

Related Questions