rustimate-client 0.1.0

WASM web client sources for rustimate
use rustimate_core::member::Member;
use std::collections::HashMap;
use uuid::Uuid;

pub(crate) struct ResultSummary {
  valid_votes: Vec<(Uuid, String, Option<f64>)>,
  invalid_votes: Vec<(Uuid, Option<String>)>,
  mean: f64,
  median: f64,
  mode: f64
}

impl ResultSummary {
  pub(crate) fn new(members: &[&Member], votes: &[(Uuid, String)]) -> Self {
    let mut valid_votes = Vec::new();
    let mut invalid_votes = Vec::new();

    for m in members {
      let hit = votes
        .iter()
        .find_map(|v| if &v.0 == m.user_id() { Some(v.1.clone()) } else { None });
      match hit {
        Some(h) => {
          let i = match h.parse::<f64>() {
            Ok(n) => Some(n),
            Err(_) => None
          };
          valid_votes.push((*m.user_id(), h, i))
        }
        None => invalid_votes.push((*m.user_id(), None))
      }
    }

    let mut numbers: Vec<f64> = valid_votes.iter().flat_map(|x| x.2).collect();
    numbers.sort_by(|a, b| a.partial_cmp(b).expect("Uncomparable?"));
    let sum = numbers.iter().sum::<f64>();

    let mean = if numbers.is_empty() { 0.0 } else { sum / (numbers.len() as f64) };
    let median = if numbers.is_empty() { 0.0 } else { numbers[numbers.len() / 2] };
    let mode = if numbers.is_empty() {
      0.0
    } else {
      let mut occurrences = HashMap::new();

      for value in numbers {
        *occurrences.entry(value.to_string()).or_insert(0) += 1;
      }

      occurrences
        .into_iter()
        .max_by_key(|&(_, count)| count)
        .map(|(val, _)| val.parse::<f64>().expect("Did it change somehow?"))
        .expect("Mode attempted with zero numbers")
    };

    Self {
      valid_votes,
      invalid_votes,
      mean,
      median,
      mode
    }
  }

  pub(crate) const fn valid_votes(&self) -> &Vec<(Uuid, String, Option<f64>)> {
    &self.valid_votes
  }

  pub(crate) const fn invalid_votes(&self) -> &Vec<(Uuid, Option<String>)> {
    &self.invalid_votes
  }

  pub(crate) const fn mean(&self) -> f64 {
    self.mean
  }

  pub(crate) const fn median(&self) -> f64 {
    self.median
  }

  pub(crate) const fn mode(&self) -> f64 {
    self.mode
  }
}