I woule like to remove an element from rust BTreeMap that I previouly found using a key.
I made up a toy example for this. Suppose I have a BTreeMap that tracks all active bank accounts (i.e. accounts with balance >=0). I charge a fee on a specific account, and if the remaining balance after fee is negative, the account is removed from the map.
The following is a working sample:
use std::collections::BTreeMap;
use std::env;
struct BankAccount {
balance: i32
}
fn charge_fee(
active_accounts: &mut BTreeMap<u32, BankAccount>, acct_id: u32, fee: i32) {
if let Some(tmp) = active_accounts.get_mut(&acct_id) {
(*tmp).balance -= fee;
if (*tmp).balance<0 {
active_accounts.remove(&acct_id);
}
}
}
fn test_charge_fee(account_id: u32, fees: i32) {
let mut active_accounts = BTreeMap::new();
active_accounts.insert(0, BankAccount{balance: 100});
active_accounts.insert(1, BankAccount{balance: 50});
active_accounts.insert(2, BankAccount{balance: 29});
active_accounts.insert(3, BankAccount{balance: 87});
println!("Active accts before charging fees:");
for (k, v) in active_accounts.iter() {
println!("{}: {}", k, v.balance);
}
charge_fee(&mut active_accounts, account_id, fees);
println!("Active accts after charging fees:");
for (k, v) in active_accounts.iter() {
println!("{}: {}", k, v.balance);
}
}
BTreeMap::remove will always perform a key search throught the entire tree, which is an unecessary cost if the key has recently been found using BTreeMap::get_mut or BTreeMap::get. It could have started just from the node where the key was found.
My questions are as folows:
Is there a nice way to remove the key-value pair from the tree without paying this cost?
Is BTreeMap the best container type for this type of application? Any suggestions for alternatives?
Did I make any mistakes?
You can use the entry API:
fn charge_fee(active_accounts: &mut BTreeMap<u32, BankAccount>, acct_id: u32, fee: i32) {
if let std::collections::btree_map::Entry::Occupied(mut entry) = active_accounts.entry(acct_id)
{
let tmp = entry.get_mut();
tmp.balance -= fee;
if tmp.balance < 0 {
entry.remove();
}
}
}
Related
Currently building a CRUD system and want to replace the selected item with the new updated item value. Since I am a noob, still need to learn lot of things, so how can I fix this. Completely confused on how to fix that.
What the current problem is that I am not able to find the item name for example bob and replace that with a new item value.
let action = std::env::args().nth(1).expect("Please provide an action");
let item = std::env::args().nth(2).expect("Please provide an item");
let _getitem = std::env::args().nth(3).expect("Please provide an item");
struct Todo {
map: HashMap<String, bool>,
}
if action == "edit" {
match todo.edit(&item, &_getitem) {
None => println!("'{}' is not present in the list", item),
Some(_) => match todo.save() {
Ok(_) => println!("todo saved"),
Err(why) => println!("An error occurred: {}", why),
},
}
}
fn edit(&mut self, key: &String, value: &String) -> Option<()> {
let elements = self.map.get_mut(key);
elements.push(value.to_string());
}
Data structure of hashmap looks like:
{"bob": true, "new": true }
I assume you want to update an existing record in a HashMap. One way to go about this is using HashMap::entry (Rust Doc):
Once you select an entry you can modify it using the Entry API. This allows you to chain updates and inserts, for example:
let mut map: HashMap<&'static str, usize> = HashMap::new();
map.insert("a", 1);
map
.entry("a")
.and_modify(|val| *val += 1);
In your case edit can look like this:
fn edit(&mut self, key: &String, value: bool) {
self.map.entry(key)
.and_modify(|val| *val = value);
}
Note that in your example the value of a HashMap must be bool but you are trying to update it to a string. You need to parse this first, e.g. using from_str (Rust Doc) or by using your own function:
fn to_bool(s: &str) -> Result<bool, ()> {
if s == "x" {
return Ok(true);
}
if s == "" {
return Ok(false);
}
Err(())
}
I've been trying to impl the push for this struct:
struct StackMin<T: std::cmp::Ord>
{
stack : Vec<T>,
min : Vec<T>
}
like this:
fn push(&mut self, item: T) {
let l = self.stack.len();
let x: T;
match l {
0 => println!("There is nothing in the stack."),
n => {
if item <= self.stack[l - 1] {
self.stack.push(item); //item moved here
self.min.push(item); // so I can't use it again here
} else {
self.stack.push(item);
}
}
}
}
The problem is item moves with the first Vec<T>::push so I can't use it immediately at the second call of push(). I thought about making a variable let a = &item and use it in the second call, but push requires "T" and not "&T".
Also, if I try to do a=self.stack[l-1], it's an error because the T type doesn't have the Copy/Clone traits.
LATER EDIT: I also need to print the last value from the min Vector. But it doesn't have the std::fmt::Display , and I don't think it can be impl!? Any ideas?
How would you approach this?
Assuming you can change the inner values of the struct StackMin, but not the trait requirements, you could do something like this:
struct MinStack<T: std::cmp::Ord> {
// T is the data you want to store
// and usize points to the smallest T
inner: Vec<(T, usize)>
}
impl<T: std::cmp::Ord> MinStack<T> {
fn push(&mut self, val: T) {
let min_index = self.inner.last()
// get last min value and its index
.map(|(_, index)| (&self.inner[*index].0, index))
// check if it is smaller then the current value
.and_then(|(prev_min, min_index)|
(prev_min < &val).then(|| *min_index)
)
// if not smaller or does not exist
// set it to the current index
.unwrap_or(self.inner.len());
self.inner.push((val, min_index));
}
}
Here is a full implementation of the MinStack challenge Rust Playground.
Let me know if i should clarify something in the above code.
Docs for the used methods:
Vec::last
bool::then
Option::map
Option::and_then
Option::unwrap_or
I'm pretty new to Rust and trying to implement some kind of database. Users should create tables by giving a table name, a vector of column names and a vector of column types (realized over an enum). Filling tables should be done by specifying csv files. However, this requires the structure of the table rows to be specified at compile time, like shown in the basic example:
#[derive(Debug, Deserialize, Eq, PartialEq)]
struct Row {
key: u32,
name: String,
comment: String
}
use std::error::Error;
use csv::ReaderBuilder;
use serde::Deserialize;
use std::fs;
fn read_from_file(path: &str) -> Result<(), Box<dyn Error>> {
let data = fs::read_to_string(path).expect("Unable to read file");
let mut rdr = ReaderBuilder::new()
.has_headers(false)
.delimiter(b'|')
.from_reader(data.as_bytes());
let mut iter = rdr.deserialize();
if let Some(result) = iter.next() {
let record:Row = result?;
println!("{:?}", record);
Ok(())
} else {
Err(From::from("expected at least one record but got none"))
}
}
Is there a possibility to use the generic table information instead of the "Row"-struct to cast the results from the deserialization? Is it possible to simply allocate memory according to the combined sizes of the column types and parse the records in? I would do something like this in C...
Is there a possibility to use the generic table information instead of the "Row"-struct to cast the results from the deserialization?
All generics replaced with concrete types at compile time. If you do not know types you will need in runtime, "generics" is not what you need.
Is it possible to simply allocate memory according to the combined sizes of the column types and parse the records in? I would do something like this in C...
I suggest using Box<dyn Any> instead, to be able to store reference of any type and, still, know what type it is.
Maintenance cost for this approach is pretty high. You have to manage each possible value type everywhere you want to use a cell's value. On the other hand, you do not need to parse value each time, just make some type checks in runtime.
I have used std::any::TypeId to identify type, but it can not be used in match expressions. You can consider using custom enum as type identifier.
use std::any::{Any, TypeId};
use std::io::Read;
use csv::Reader;
#[derive(Default)]
struct Table {
name: String,
headers: Vec<(String, TypeId)>,
data: Vec<Vec<Box<dyn Any>>>,
}
impl Table {
fn add_header(&mut self, header: String, _type: TypeId) {
self.headers.push((header, _type));
}
fn populate_data<R: Read>(
&mut self,
rdr: &mut Reader<R>,
) -> Result<(), Box<dyn std::error::Error>> {
for record in rdr.records() {
let record = record?;
let mut row: Vec<Box<dyn Any>> = vec![];
for (&(_, type_id), value) in self.headers.iter().zip(record.iter()) {
if type_id == TypeId::of::<u32>() {
row.push(Box::new(value.parse::<u32>()?));
} else if type_id == TypeId::of::<String>() {
row.push(Box::new(value.to_owned()));
}
}
self.data.push(row);
}
Ok(())
}
}
impl std::fmt::Display for Table {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Table: {}", self.name)?;
for (name, _) in self.headers.iter() {
write!(f, "{}, ", name)?;
}
writeln!(f)?;
for row in self.data.iter() {
for cell in row.iter() {
if let Some(&value) = cell.downcast_ref::<u32>() {
write!(f, "{}, ", value)?;
} else if let Some(value) = cell.downcast_ref::<String>() {
write!(f, "{}, ", value)?;
}
}
writeln!(f)?;
}
Ok(())
}
}
fn main() {
let mut table: Table = Default::default();
table.name = "Foo".to_owned();
table.add_header("key".to_owned(), TypeId::of::<u32>());
table.add_header("name".to_owned(), TypeId::of::<String>());
table.add_header("comment".to_owned(), TypeId::of::<String>());
let data = "\
key,name,comment
1,foo,foo comment
2,bar,bar comment
";
let mut rdr = Reader::from_reader(data.as_bytes());
table.populate_data(&mut rdr).unwrap();
print!("{}", table);
}
I want to create a vector with all of the matching field id from the struct, process that new vector and then repeat the process. Basically grouping together the structs with matching field id.
Is there a way to do this by not using the unstable feature drain_filter?
#![feature(drain_filter)]
#[derive(Debug)]
struct Person {
id: u32,
}
fn main() {
let mut people = vec![];
for p in 0..10 {
people.push(Person { id: p });
}
while !people.is_empty() {
let first_person_id = people.first().unwrap().id;
let drained: Vec<Person> = people.drain_filter(|p| p.id == first_person_id).collect();
println!("{:#?}", drained);
}
}
Playground
If you are looking to group your vector by the person id, it's likely to be more efficient using a HashMap from id to Vec<Person>, where each id hold a vector of persons. And then you can loop through the HashMap and process each vector / group. This is potentially more efficient than draining people in each iteration, which in worst case has O(N^2) time complexity while with a HashMap the time complexity is O(N).
#![feature(drain_filter)]
use std::collections::HashMap;
#[derive(Debug)]
struct Person {
id: u32,
}
fn main() {
let mut people = vec![];
let mut groups: HashMap<u32, Vec<Person>> = HashMap::new();
for p in 0..10 {
people.push(Person { id: p });
}
people.into_iter().for_each(|person| {
let group = groups.entry(person.id).or_insert(vec![]);
group.push(person);
});
for (_id, group) in groups {
println!("{:#?}", group);
}
}
Playground
I have a program that revolves around one shared data structure, proposing changes to the data, and then applying these changes at a later stage. These proposed changes hold references to the core object.
In C++ or another language, I would simply make the reference non-const, then mutate it when I need to. But Rust doesn't play well with this approach. (I asked about this in IRC earlier today, but sadly I'm still stuck.)
To help, I made a distilled example for booking tickets in a theatre, where theatre is the data structure, the Bookings are proposed changes, and the run method would be applying them if I could figure out how to get it to work!
Firstly, defining some data structures. A theatre has many rows, which have many seats each:
use std::sync::{Arc, RwLock};
use std::thread;
struct Theatre { rows: Vec<Row> }
struct Row { seats: Vec<Seat> }
struct Seat {
number: i32,
booked: bool,
}
impl Seat {
fn new(number: i32) -> Seat {
Seat { number: number, booked: false }
}
fn book(&mut self) {
self.booked = true;
}
}
Here, the get_booking method searches for a seat, returning a Booking with a reference to the seat it finds.
impl Theatre {
fn get_booking<'t>(&'t self, number: i32) -> Option<Booking<'t>> {
for row in self.rows.iter() {
for seat in row.seats.iter() {
if seat.number == number && seat.booked == false {
return Some(Booking { seats: vec![ seat ] })
}
}
}
None
}
}
But this is where I get stuck. The run method has mutable access to the overall theatre (from its parameter), and it knows which seat to mutate (self). But since self isn't mutable, even though the theatre that contains it is, it can't be mutated.
struct Booking<'t> {
seats: Vec<&'t Seat>
}
impl<'t> Booking<'t> {
fn describe(&self) {
let seats: Vec<_> = self.seats.iter().map(|s| s.number).collect();
println!("You want to book seats: {:?}", seats);
}
fn run(&self, _theatre: &mut Theatre) {
let mut seat = ??????;
seat.book();
}
}
Finally, a main method that would use it if it worked.
fn main() {
// Build a theatre (with only one seat... small theatre)
let theatre = Theatre { rows: vec![ Row { seats: vec![ Seat::new(7) ] } ] };
let wrapper = Arc::new(RwLock::new(theatre));
// Try to book a seat in another thread
let thread = thread::spawn(move || {
let desired_seat_number = 7;
let t = wrapper.read().unwrap();
let booking = t.get_booking(desired_seat_number).expect("No such seat!");
booking.describe();
let mut tt = wrapper.write().unwrap();
booking.run(&mut tt); // this is never actually reached because we still have the read lock
});
thread.join().unwrap();
}
What's annoying is that I know exactly why my current code doesn't work - I just can't figure out how Rust wants my program formatted instead. There are some things I don't want to do:
The simplest solution is to have Booking hold an index to its seat, instead of a reference: in this case, with row and seat usize fields. However, although my theatre uses O(1) vectors, I'd also like to reference a value in the middle of a large tree, where having to iterate to find the value would be much more expensive. This would also mean that you couldn't, say, get the seat number (in the describe function) without having to pass in the entire Theatre.
It would also be solved by having a Booking hold a mutable reference to the seat, which I could just then mutate as normal. However, this would mean I could only have one proposed change at a time: I couldn't, for example, have a list of bookings and apply them all at once, or have two bookings and only apply one.
I feel like I'm very close to having something that Rust will accept, but don't quite know how to structure my program to accommodate it. So, any pointers? (pun intended)
First, here's the code:
use std::sync::{Arc, RwLock};
use std::thread;
use std::sync::atomic::{AtomicBool, Ordering};
struct Theatre { rows: Vec<Row> }
struct Row { seats: Vec<Seat> }
struct Seat {
number: i32,
booked: AtomicBool,
}
impl Seat {
fn new(number: i32) -> Seat {
Seat { number: number, booked: AtomicBool::new(false) }
}
fn book(&self) {
self.booked.store(true, Ordering::Release);
println!("Booked seat: {:?}", self.number);
}
}
impl Theatre {
fn get_booking<'t>(&'t self, number: i32) -> Option<Booking<'t>> {
for row in self.rows.iter() {
for seat in row.seats.iter() {
if seat.number == number && seat.booked.load(Ordering::Acquire) == false {
return Some(Booking { seats: vec![ seat ] })
}
}
}
None
}
}
struct Booking<'t> {
seats: Vec<&'t Seat>
}
impl<'t> Booking<'t> {
fn describe(&self) {
let seats: Vec<_> = self.seats.iter().map(|s| s.number).collect();
println!("You want to book seats: {:?}", seats);
}
fn run(&self) {
for seat in self.seats.iter() {
seat.book();
}
}
}
fn main() {
// Build a theatre (with only one seat... small theatre)
let theatre = Theatre { rows: vec![ Row { seats: vec![ Seat::new(7) ] } ] };
let wrapper = Arc::new(RwLock::new(theatre));
// Try to book a seat in another thread
let thread = thread::spawn(move || {
let desired_seat_number = 7;
let t = wrapper.read().unwrap();
let booking = t.get_booking(desired_seat_number).expect("No such seat!");
booking.describe();
booking.run();
});
thread.join().unwrap();
}
View on playpen
There are two important changes:
The booked field was changed from bool to AtomicBool. The atomic types provide a store method that is available on immutable references. Therefore, we can make Seat::book() take self by immutable reference. If you have a more complex type that is not covered by the atomic types, you should instead use a Mutex or a RwLock.
I removed the &mut Theatre parameter on Booking::run(). If this is not acceptable, please leave a comment to explain why you need that reference.
As you found, you cannot have both a read lock and a write lock active at the same time on a RwLock. However, a Booking cannot live longer than the read lock on the Theatre, because it contains references inside the Theatre. Once you release the read lock, you cannot guarantee that the references you obtained will remain valid when you acquired another lock later on. If that's a problem, consider using Arc instead of simple borrowed pointers (&).