vrl/stdlib/
tally_value.rs

1use crate::compiler::prelude::*;
2
3fn tally_value(array: Value, value: &Value) -> Resolved {
4    let array = array.try_array()?;
5    Ok(array.iter().filter(|&v| v == value).count().into())
6}
7
8#[derive(Clone, Copy, Debug)]
9pub struct TallyValue;
10
11impl Function for TallyValue {
12    fn identifier(&self) -> &'static str {
13        "tally_value"
14    }
15
16    fn usage(&self) -> &'static str {
17        "Counts the number of times a specific value appears in the provided array."
18    }
19
20    fn category(&self) -> &'static str {
21        Category::Enumerate.as_ref()
22    }
23
24    fn return_kind(&self) -> u16 {
25        kind::INTEGER
26    }
27
28    fn examples(&self) -> &'static [Example] {
29        &[example! {
30            title: "count matching values",
31            source: r#"tally_value(["foo", "bar", "foo", "baz"], "foo")"#,
32            result: Ok("2"),
33        }]
34    }
35
36    fn compile(
37        &self,
38        _state: &state::TypeState,
39        _ctx: &mut FunctionCompileContext,
40        arguments: ArgumentList,
41    ) -> Compiled {
42        let array = arguments.required("array");
43        let value = arguments.required("value");
44
45        Ok(TallyValueFn { array, value }.as_expr())
46    }
47
48    fn parameters(&self) -> &'static [Parameter] {
49        const PARAMETERS: &[Parameter] = &[
50            Parameter::required("array", kind::ARRAY, "The array to search through."),
51            Parameter::required(
52                "value",
53                kind::ANY,
54                "The value to count occurrences of in the array.",
55            ),
56        ];
57        PARAMETERS
58    }
59}
60
61#[derive(Debug, Clone)]
62pub(crate) struct TallyValueFn {
63    array: Box<dyn Expression>,
64    value: Box<dyn Expression>,
65}
66
67impl FunctionExpression for TallyValueFn {
68    fn resolve(&self, ctx: &mut Context) -> Resolved {
69        let array = self.array.resolve(ctx)?;
70        let value = self.value.resolve(ctx)?;
71
72        tally_value(array, &value)
73    }
74
75    fn type_def(&self, _: &state::TypeState) -> TypeDef {
76        TypeDef::integer().infallible()
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use crate::value;
84
85    test_function![
86        tally_value => TallyValue;
87
88        default {
89            args: func_args![
90                array: value!(["bar", "foo", "baz", "foo"]),
91                value: value!("foo"),
92            ],
93            want: Ok(value!(2)),
94            tdef: TypeDef::integer().infallible(),
95        }
96    ];
97}