@@ -388,90 +388,151 @@ <h3 id="borrowing-during-iteration"><a class="header" href="#borrowing-during-it
388388}
389389< span class ="boring "> }</ span > </ code > </ pre > </ pre >
390390< hr />
391- < h2 id ="exercise-build-a-cache-system "> < a class ="header " href ="#exercise-build-a-cache-system "> Exercise: Build a Cache System</ a > </ h2 >
392- < p > Create an LRU (Least Recently Used) cache with expiration:</ p >
393- < pre > < pre class ="playground "> < code class ="language-rust "> use std::collections::{HashMap, VecDeque};
394- use std::time::{Duration, Instant};
395-
396- struct CacheEntry<V> {
397- value: V,
398- last_accessed: Instant,
399- expires_at: Option<Instant>,
391+ < h2 id ="exercise-student-grade-management-system "> < a class ="header " href ="#exercise-student-grade-management-system "> Exercise: Student Grade Management System</ a > </ h2 >
392+ < p > Create a system that manages student grades using HashMap and HashSet to practice collections operations and the Entry API:</ p >
393+ < pre > < pre class ="playground "> < code class ="language-rust "> use std::collections::{HashMap, HashSet};
394+
395+ #[derive(Debug)]
396+ struct GradeBook {
397+ // Student name -> HashMap of (subject -> grade)
398+ grades: HashMap<String, HashMap<String, f64>>,
399+ // Set of all subjects offered
400+ subjects: HashSet<String>,
400401}
401402
402- struct LRUCache<K: Clone + Eq + std::hash::Hash, V> {
403- capacity: usize,
404- cache: HashMap<K, CacheEntry<V>>,
405- access_order: VecDeque<K>,
406- }
407-
408- impl<K: Clone + Eq + std::hash::Hash, V: Clone> LRUCache<K, V> {
409- fn new(capacity: usize) -> Self {
410- LRUCache {
411- capacity,
412- cache: HashMap::new(),
413- access_order: VecDeque::new(),
403+ impl GradeBook {
404+ fn new() -> Self {
405+ GradeBook {
406+ grades: HashMap::new(),
407+ subjects: HashSet::new(),
414408 }
415409 }
416-
417- fn get(&mut self, key: &K) -> Option<&V> {
418- // TODO: Check if key exists
419- // TODO: Check if entry is expired (remove if expired)
420- // TODO: Update last_accessed time
421- // TODO: Move key to end of access_order (most recently used)
422- // TODO: Return the value
410+
411+ fn add_subject(&mut self, subject: String) {
412+ // TODO: Add subject to the subjects set
423413 todo!()
424414 }
425-
426- fn insert(&mut self, key: K, value: V, ttl: Option<Duration>) {
427- // TODO: If at capacity, remove least recently used item
428- // TODO: Create cache entry with expiration if ttl provided
429- // TODO: Add to cache and access_order
415+
416+ fn add_grade(&mut self, student: String, subject: String, grade: f64) {
417+ // TODO: Add a grade for a student in a subject
418+ // Hints:
419+ // 1. Add subject to subjects set
420+ // 2. Use entry() API to get or create the student's grade map
421+ // 3. Insert the grade for the subject
430422 todo!()
431423 }
432-
433- fn remove(&mut self, key: &K) -> Option<V> {
434- // TODO: Remove from cache and access_order
435- // TODO: Return the value if it existed
424+
425+ fn get_student_average(&self, student: &str) -> Option<f64> {
426+ // TODO: Calculate average grade for a student across all their subjects
427+ // Return None if student doesn't exist
428+ // Hint: Use .values() and iterator methods
436429 todo!()
437430 }
438-
439- fn clear_expired(&mut self) {
440- // TODO: Remove all expired entries
431+
432+ fn get_subject_average(&self, subject: &str) -> Option<f64> {
433+ // TODO: Calculate average grade for a subject across all students
434+ // Return None if no students have grades in this subject
441435 todo!()
442436 }
443-
444- fn stats(&self) -> (usize, usize) {
445- // Return (current_size, capacity)
446- (self.cache.len(), self.capacity)
437+
438+ fn get_students_in_subject(&self, subject: &str) -> Vec<&String> {
439+ // TODO: Return list of students who have a grade in the given subject
440+ // Hint: Filter students who have this subject in their grade map
441+ todo!()
442+ }
443+
444+ fn get_top_students(&self, n: usize) -> Vec<(String, f64)> {
445+ // TODO: Return top N students by average grade
446+ // Format: Vec<(student_name, average_grade)>
447+ // Hint: Calculate averages, collect into Vec, sort, and take top N
448+ todo!()
449+ }
450+
451+ fn remove_student(&mut self, student: &str) -> bool {
452+ // TODO: Remove a student and all their grades
453+ // Return true if student existed, false otherwise
454+ todo!()
455+ }
456+
457+ fn list_subjects(&self) -> Vec<&String> {
458+ // TODO: Return all subjects as a sorted vector
459+ todo!()
447460 }
448461}
449462
450463fn main() {
451- let mut cache = LRUCache::new(3);
452-
453- // Test basic operations
454- cache.insert("user:1", "Alice", Some(Duration::from_secs(60)));
455- cache.insert("user:2", "Bob", None); // No expiration
456- cache.insert("user:3", "Charlie", Some(Duration::from_secs(5)));
457-
458- // Access user:1 to make it most recently used
459- println!("Got: {:?}", cache.get(&"user:1"));
460-
461- // Add one more - should evict user:2 (least recently used)
462- cache.insert("user:4", "David", None);
463-
464- // Try to get user:2 - should be None (evicted)
465- println!("User 2 (should be evicted): {:?}", cache.get(&"user:2"));
466-
467- let (size, capacity) = cache.stats();
468- println!("Cache stats - Size: {}/{}", size, capacity);
464+ let mut gradebook = GradeBook::new();
465+
466+ // Add subjects
467+ gradebook.add_subject("Math".to_string());
468+ gradebook.add_subject("English".to_string());
469+ gradebook.add_subject("Science".to_string());
470+
471+ // Add grades for students
472+ gradebook.add_grade("Alice".to_string(), "Math".to_string(), 95.0);
473+ gradebook.add_grade("Alice".to_string(), "English".to_string(), 87.0);
474+ gradebook.add_grade("Bob".to_string(), "Math".to_string(), 82.0);
475+ gradebook.add_grade("Bob".to_string(), "Science".to_string(), 91.0);
476+ gradebook.add_grade("Charlie".to_string(), "English".to_string(), 78.0);
477+ gradebook.add_grade("Charlie".to_string(), "Science".to_string(), 85.0);
478+
479+ // Test the methods
480+ if let Some(avg) = gradebook.get_student_average("Alice") {
481+ println!("Alice's average: {:.2}", avg);
482+ }
483+
484+ if let Some(avg) = gradebook.get_subject_average("Math") {
485+ println!("Math class average: {:.2}", avg);
486+ }
487+
488+ let math_students = gradebook.get_students_in_subject("Math");
489+ println!("Students in Math: {:?}", math_students);
490+
491+ let top_students = gradebook.get_top_students(2);
492+ println!("Top 2 students: {:?}", top_students);
493+
494+ println!("All subjects: {:?}", gradebook.list_subjects());
469495}</ code > </ pre > </ pre >
470- < p > < strong > Hints:</ strong > </ p >
496+ < p > < strong > Implementation Hints:</ strong > </ p >
497+ < ol >
498+ < li >
499+ < p > < strong > add_grade() method:</ strong > </ p >
500+ < ul >
501+ < li > Use < code > self.grades.entry(student).or_insert_with(HashMap::new)</ code > </ li >
502+ < li > Then insert the grade: < code > .insert(subject, grade)</ code > </ li >
503+ </ ul >
504+ </ li >
505+ < li >
506+ < p > < strong > get_student_average():</ strong > </ p >
471507< ul >
472- < li > Use < code > VecDeque::retain()</ code > to keep only non-expired keys</ li >
473- < li > HashMap’s < code > entry()</ code > API is useful for get-or-insert patterns</ li >
474- < li > Remember to update both the HashMap and VecDeque when modifying</ li >
508+ < li > Use < code > self.grades.get(student)?</ code > to get the student’s grades</ li >
509+ < li > Use < code > .values().sum::<f64>() / values.len() as f64</ code > </ li >
510+ </ ul >
511+ </ li >
512+ < li >
513+ < p > < strong > get_subject_average():</ strong > </ p >
514+ < ul >
515+ < li > Iterate through all students: < code > self.grades.iter()</ code > </ li >
516+ < li > Filter students who have this subject: < code > filter_map(|(_, grades)| grades.get(subject))</ code > </ li >
517+ < li > Calculate average from the filtered grades</ li >
518+ </ ul >
519+ </ li >
520+ < li >
521+ < p > < strong > get_top_students():</ strong > </ p >
522+ < ul >
523+ < li > Use < code > map()</ code > to convert students to (name, average) pairs</ li >
524+ < li > Use < code > collect::<Vec<_>>()</ code > and < code > sort_by()</ code > with float comparison</ li >
525+ < li > Use < code > take(n)</ code > to get top N</ li >
526+ </ ul >
527+ </ li >
528+ </ ol >
529+ < p > < strong > What you’ll learn:</ strong > </ p >
530+ < ul >
531+ < li > HashMap’s Entry API for efficient insertions</ li >
532+ < li > HashSet for tracking unique values</ li >
533+ < li > Nested HashMap structures</ li >
534+ < li > Iterator methods for data processing</ li >
535+ < li > Working with Option types from HashMap lookups</ li >
475536</ ul >
476537< hr />
477538< h2 id ="key-takeaways "> < a class ="header " href ="#key-takeaways "> Key Takeaways</ a > </ h2 >
@@ -514,6 +575,22 @@ <h2 id="key-takeaways"><a class="header" href="#key-takeaways">Key Takeaways</a>
514575
515576 </ div >
516577
578+ <!-- Livereload script (if served using the cli tool) -->
579+ < script >
580+ const wsProtocol = location . protocol === 'https:' ? 'wss:' : 'ws:' ;
581+ const wsAddress = wsProtocol + "//" + location . host + "/" + "__livereload" ;
582+ const socket = new WebSocket ( wsAddress ) ;
583+ socket . onmessage = function ( event ) {
584+ if ( event . data === "reload" ) {
585+ socket . close ( ) ;
586+ location . reload ( ) ;
587+ }
588+ } ;
589+
590+ window . onbeforeunload = function ( ) {
591+ socket . close ( ) ;
592+ }
593+ </ script >
517594
518595
519596
0 commit comments