-
-
Save djhworld/d49f9acae96e32465e05f9d16930448c to your computer and use it in GitHub Desktop.
package main | |
import ( | |
"bufio" | |
"fmt" | |
"os" | |
) | |
type Counter struct { | |
uniqueItems map[string]int | |
} | |
func NewCounter() *Counter { | |
c := new(Counter) | |
c.uniqueItems = make(map[string]int) | |
return c | |
} | |
func (c *Counter) Add(item string) { | |
if _, ok := c.uniqueItems[item]; !ok { | |
c.uniqueItems[item] = 1 | |
} else { | |
c.uniqueItems[item] += 1 | |
} | |
} | |
func (c *Counter) Render() { | |
for k, v := range c.uniqueItems { | |
fmt.Printf("%d\t%s\n", v, k) | |
} | |
} | |
func main() { | |
counter := NewCounter() | |
scanner := bufio.NewScanner(os.Stdin) | |
for scanner.Scan() { | |
counter.Add(scanner.Text()) | |
} | |
counter.Render() | |
} |
use std::collections::HashMap; | |
use std::io; | |
struct Counter { | |
items: HashMap<String, usize>, | |
} | |
impl Counter { | |
fn new() -> Counter { | |
return Counter { | |
items: HashMap::with_capacity(1000), | |
}; | |
} | |
fn add(&mut self, st: String) { | |
let x = self.items.entry(st).or_insert(0); | |
*x += 1; | |
} | |
fn render(&self) { | |
for (key, val) in &self.items { | |
println!("{}\t{}", val, key); | |
} | |
} | |
} | |
fn main() { | |
let mut c = Counter::new(); | |
loop { | |
let mut line = String::new(); | |
let r = io::stdin().read_line(&mut line); | |
match r { | |
Ok(0) => break, | |
Ok(_) => { | |
line.pop(); //strip newline char | |
c.add(line); | |
} | |
Err(err) => { | |
println!("{}", err); | |
break; | |
} | |
}; | |
} | |
c.render(); | |
} |
For the Rust version, deriving Default
and using the HashMap::entry
API might be more idiomatic:
#[derive(Debug, Clone, Default)]
pub struct Counter {
pub items: HashMap<String, usize>,
}
impl Counter {
pub fn count_rdr(&mut self, rdr: &mut impl BufRead) -> io::Result<()> {
let mut buffer = String::new();
loop {
buffer.clear();
match rdr.read_line(&mut buffer) {
Ok(0) => break,
Ok(_) => self.add(buffer.trim_end()),
Err(e) => {
eprintln!("ERR: {}", e);
break;
}
}
}
Ok(())
}
fn add(&mut self, line: &str) {
let count = self.items.entry(line.to_string()).or_insert(0);
*count += 1;
}
}
Using the entry API costs an allocation on every lookup which can be a severe performance penalty on something like this.
@Freaky good point! Your mention of allocation gave me the idea to look into refactoring the Counter to take a string slice and use that as the key instead of an owned String. This resulted in about a 34% improvement in counting performance over the get_mut
implementation using owned Strings. I haven't looked into the performance implications of reading all of stdin
before doing the counting, instead of using buffered reads. I suspect that clearing the buffer on each line read would incur some overhead, though. Interesting stuff.
I've made a follow up gist with the improvements suggested here https://gist.github.com/djhworld/ced7eabca8e0c06d5b65917e87fb8745
I'll look into changing the go code too!