hawk_data/
stats_opts.rs

1use serde_json::Value;
2use crate::Error;
3
4/// 統計操作を適用する
5pub fn apply_stats_operation(data: &[Value], operation: &str, field: Option<&str>) -> Result<Value, Error> {
6    match operation {
7        "unique" => apply_unique(data, field),
8        "sort" => apply_sort(data, field),
9        "median" => apply_median(data, field),
10        "stddev" => apply_stddev(data, field),
11        "length" => Ok(Value::Number(serde_json::Number::from(data.len()))),
12        _ => Err(Error::StringOperation(format!("Unknown stats operation: {}", operation))),
13    }
14}
15
16/// ユニーク値を取得
17fn apply_unique(data: &[Value], field: Option<&str>) -> Result<Value, Error> {
18    use std::collections::HashSet;
19    
20    let mut unique_values = HashSet::new();
21    let mut result = Vec::new();
22    
23    for item in data {
24        let value_to_check = if let Some(field_name) = field {
25            // フィールド指定がある場合
26            item.get(field_name).unwrap_or(&Value::Null).clone()
27        } else {
28            // フィールド指定がない場合は値そのもの
29            item.clone()
30        };
31        
32        // JSON値をハッシュ可能な文字列に変換
33        let key = serde_json::to_string(&value_to_check).unwrap_or_default();
34        
35        if unique_values.insert(key) {
36            result.push(value_to_check);
37        }
38    }
39    
40    Ok(Value::Array(result))
41}
42
43/// ソート
44fn apply_sort(data: &[Value], field: Option<&str>) -> Result<Value, Error> {
45    let mut sorted_data = data.to_vec();
46    
47    sorted_data.sort_by(|a, b| {
48        let val_a = if let Some(field_name) = field {
49            a.get(field_name).unwrap_or(&Value::Null)
50        } else {
51            a
52        };
53        
54        let val_b = if let Some(field_name) = field {
55            b.get(field_name).unwrap_or(&Value::Null)
56        } else {
57            b
58        };
59        
60        compare_json_values(val_a, val_b)
61    });
62    
63    Ok(Value::Array(sorted_data))
64}
65
66/// 中央値を計算
67fn apply_median(data: &[Value], field: Option<&str>) -> Result<Value, Error> {
68    let mut numbers = extract_numbers(data, field)?;
69    
70    if numbers.is_empty() {
71        return Ok(Value::Null);
72    }
73    
74    numbers.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
75    
76    let len = numbers.len();
77    let median = if len % 2 == 0 {
78        // 偶数個の場合は中央2つの平均
79        (numbers[len / 2 - 1] + numbers[len / 2]) / 2.0
80    } else {
81        // 奇数個の場合は中央値
82        numbers[len / 2]
83    };
84    
85    Ok(Value::Number(serde_json::Number::from_f64(median).unwrap()))
86}
87
88/// 標準偏差を計算
89fn apply_stddev(data: &[Value], field: Option<&str>) -> Result<Value, Error> {
90    let numbers = extract_numbers(data, field)?;
91    
92    if numbers.len() < 2 {
93        return Ok(Value::Null);
94    }
95    
96    let mean = numbers.iter().sum::<f64>() / numbers.len() as f64;
97    let variance = numbers.iter()
98        .map(|x| (x - mean).powi(2))
99        .sum::<f64>() / (numbers.len() - 1) as f64; // 標本標準偏差
100    
101    let stddev = variance.sqrt();
102    
103    Ok(Value::Number(serde_json::Number::from_f64(stddev).unwrap()))
104}
105
106/// 数値を抽出
107fn extract_numbers(data: &[Value], field: Option<&str>) -> Result<Vec<f64>, Error> {
108    let mut numbers = Vec::new();
109    
110    for item in data {
111        let value = if let Some(field_name) = field {
112            item.get(field_name).unwrap_or(&Value::Null)
113        } else {
114            item
115        };
116        
117        if let Some(num) = value.as_f64() {
118            numbers.push(num);
119        }
120    }
121    
122    Ok(numbers)
123}
124
125/// JSON値の比較
126fn compare_json_values(a: &Value, b: &Value) -> std::cmp::Ordering {
127    use std::cmp::Ordering;
128    
129    match (a, b) {
130        (Value::Number(n1), Value::Number(n2)) => {
131            n1.as_f64().unwrap_or(0.0).partial_cmp(&n2.as_f64().unwrap_or(0.0)).unwrap_or(Ordering::Equal)
132        },
133        (Value::String(s1), Value::String(s2)) => s1.cmp(s2),
134        (Value::Bool(b1), Value::Bool(b2)) => b1.cmp(b2),
135        (Value::Null, Value::Null) => Ordering::Equal,
136        (Value::Null, _) => Ordering::Less,
137        (_, Value::Null) => Ordering::Greater,
138        // 異なる型の場合は型名で比較
139        _ => get_type_priority(a).cmp(&get_type_priority(b)),
140    }
141}
142
143/// 型の優先順位
144fn get_type_priority(value: &Value) -> u8 {
145    match value {
146        Value::Null => 0,
147        Value::Bool(_) => 1,
148        Value::Number(_) => 2,
149        Value::String(_) => 3,
150        Value::Array(_) => 4,
151        Value::Object(_) => 5,
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn test_unique_operation() {
161        let data = vec![
162            Value::String("apple".to_string()),
163            Value::String("banana".to_string()),
164            Value::String("apple".to_string()),
165            Value::String("cherry".to_string()),
166        ];
167        
168        let result = apply_unique(&data, None).unwrap();
169        if let Value::Array(arr) = result {
170            assert_eq!(arr.len(), 3); // apple, banana, cherry
171        } else {
172            panic!("Expected array result");
173        }
174    }
175
176    #[test]
177    fn test_sort_numbers() {
178        let data = vec![
179            Value::Number(3.into()),
180            Value::Number(1.into()),
181            Value::Number(4.into()),
182            Value::Number(2.into()),
183        ];
184        
185        let result = apply_sort(&data, None).unwrap();
186        if let Value::Array(arr) = result {
187            assert_eq!(arr[0], Value::Number(1.into()));
188            assert_eq!(arr[1], Value::Number(2.into()));
189            assert_eq!(arr[2], Value::Number(3.into()));
190            assert_eq!(arr[3], Value::Number(4.into()));
191        } else {
192            panic!("Expected array result");
193        }
194    }
195
196    #[test]
197    fn test_median_even() {
198        let data = vec![
199            Value::Number(1.into()),
200            Value::Number(2.into()),
201            Value::Number(4.into()),
202            Value::Number(5.into()),
203        ];
204        
205        let result = apply_median(&data, None).unwrap();
206        assert_eq!(result, Value::Number(serde_json::Number::from_f64(3.0).unwrap()));
207    }
208
209    #[test]
210    fn test_stddev() {
211        let data = vec![
212            Value::Number(1.into()),
213            Value::Number(2.into()),
214            Value::Number(3.into()),
215            Value::Number(4.into()),
216            Value::Number(5.into()),
217        ];
218        
219        let result = apply_stddev(&data, None).unwrap();
220        // 標本標準偏差 ≈ 1.58
221        if let Value::Number(n) = result {
222            let stddev = n.as_f64().unwrap();
223            assert!((stddev - 1.58).abs() < 0.1);
224        } else {
225            panic!("Expected number result");
226        }
227    }
228
229    #[test]
230    fn test_unique_with_field() {
231        let data = vec![
232            serde_json::json!({"name": "Alice", "age": 30}),
233            serde_json::json!({"name": "Bob", "age": 25}),
234            serde_json::json!({"name": "Alice", "age": 35}),
235        ];
236        
237        let result = apply_unique(&data, Some("name")).unwrap();
238        if let Value::Array(arr) = result {
239            assert_eq!(arr.len(), 2); // Alice, Bob
240        } else {
241            panic!("Expected array result");
242        }
243    }
244}