nu_data/utils/
internal.rs

1#![allow(clippy::type_complexity)]
2
3use crate::value::unsafe_compute_values;
4use derive_new::new;
5use nu_errors::ShellError;
6use nu_protocol::hir::Operator;
7use nu_protocol::{UntaggedValue, Value};
8use nu_source::{SpannedItem, Tag, TaggedItem};
9use nu_value_ext::ValueExt;
10
11#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)]
12pub struct Labels {
13    pub x: Vec<String>,
14    pub y: Vec<String>,
15}
16
17impl Labels {
18    pub fn at(&self, idx: usize) -> Option<&str> {
19        self.x.get(idx).map(|k| &k[..])
20    }
21
22    pub fn at_split(&self, idx: usize) -> Option<&str> {
23        self.y.get(idx).map(|k| &k[..])
24    }
25
26    pub fn grouped(&self) -> impl Iterator<Item = &String> {
27        self.x.iter()
28    }
29
30    pub fn grouping_total(&self) -> Value {
31        UntaggedValue::int(self.x.len() as i64).into_untagged_value()
32    }
33
34    pub fn splits(&self) -> impl Iterator<Item = &String> {
35        self.y.iter()
36    }
37
38    pub fn splits_total(&self) -> Value {
39        UntaggedValue::big_int(self.y.len()).into_untagged_value()
40    }
41}
42
43#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)]
44pub struct Range {
45    pub start: Value,
46    pub end: Value,
47}
48
49fn formula(
50    acc_begin: Value,
51    calculator: Box<dyn Fn(Vec<&Value>) -> Result<Value, ShellError> + Send + Sync + 'static>,
52) -> Box<dyn Fn(&Value, Vec<&Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
53    Box::new(move |acc, datax| -> Result<Value, ShellError> {
54        let result = match unsafe_compute_values(Operator::Multiply, acc, &acc_begin) {
55            Ok(v) => v.into_untagged_value(),
56            Err((left_type, right_type)) => {
57                return Err(ShellError::coerce_error(
58                    left_type.spanned_unknown(),
59                    right_type.spanned_unknown(),
60                ))
61            }
62        };
63
64        match calculator(datax) {
65            Ok(total) => Ok(
66                match unsafe_compute_values(Operator::Plus, &result, &total) {
67                    Ok(v) => v.into_untagged_value(),
68                    Err((left_type, right_type)) => {
69                        return Err(ShellError::coerce_error(
70                            left_type.spanned_unknown(),
71                            right_type.spanned_unknown(),
72                        ))
73                    }
74                },
75            ),
76            Err(reason) => Err(reason),
77        }
78    })
79}
80
81pub fn reducer_for(
82    command: &Reduction,
83) -> Box<dyn Fn(&Value, Vec<&Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
84    match command {
85        Reduction::Accumulate => Box::new(formula(
86            UntaggedValue::int(1).into_untagged_value(),
87            Box::new(sum),
88        )),
89        Reduction::Count => Box::new(formula(
90            UntaggedValue::int(0).into_untagged_value(),
91            Box::new(sum),
92        )),
93    }
94}
95
96pub fn max(values: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
97    let tag = tag.into();
98
99    let mut x = UntaggedValue::int(0);
100
101    for split in values.table_entries() {
102        match split.value {
103            UntaggedValue::Table(ref values) => {
104                let inner = inner_max(values)?;
105
106                if let Ok(greater_than) =
107                    crate::value::compare_values(Operator::GreaterThan, &inner.value, &x)
108                {
109                    if greater_than {
110                        x = inner.value.clone();
111                    }
112                } else {
113                    return Err(ShellError::unexpected(format!(
114                        "Could not compare\nleft: {:?}\nright: {:?}",
115                        inner.value, x
116                    )));
117                }
118            }
119            _ => {
120                return Err(ShellError::labeled_error(
121                    "Attempted to compute the sum of a value that cannot be summed.",
122                    "value appears here",
123                    split.tag.span,
124                ))
125            }
126        }
127    }
128
129    Ok(x.into_value(&tag))
130}
131
132pub fn inner_max(data: &[Value]) -> Result<Value, ShellError> {
133    let mut biggest = data
134        .first()
135        .ok_or_else(|| {
136            ShellError::unexpected("Cannot perform aggregate math operation on empty data")
137        })?
138        .value
139        .clone();
140
141    for value in data {
142        if let Ok(greater_than) =
143            crate::value::compare_values(Operator::GreaterThan, &value.value, &biggest)
144        {
145            if greater_than {
146                biggest = value.value.clone();
147            }
148        } else {
149            return Err(ShellError::unexpected(format!(
150                "Could not compare\nleft: {:?}\nright: {:?}",
151                biggest, value.value
152            )));
153        }
154    }
155    Ok(Value {
156        value: biggest,
157        tag: Tag::unknown(),
158    })
159}
160
161pub fn sum(data: Vec<&Value>) -> Result<Value, ShellError> {
162    let mut acc = UntaggedValue::int(0);
163
164    for value in data {
165        match value.value {
166            UntaggedValue::Primitive(_) => {
167                acc = match unsafe_compute_values(Operator::Plus, &acc, value) {
168                    Ok(v) => v,
169                    Err((left_type, right_type)) => {
170                        return Err(ShellError::coerce_error(
171                            left_type.spanned_unknown(),
172                            right_type.spanned_unknown(),
173                        ))
174                    }
175                };
176            }
177            _ => {
178                return Err(ShellError::labeled_error(
179                    "Attempted to compute the sum of a value that cannot be summed.",
180                    "value appears here",
181                    value.tag.span,
182                ))
183            }
184        }
185    }
186    Ok(acc.into_untagged_value())
187}
188
189pub fn sort_columns(
190    values: &[String],
191    format: &Option<Box<dyn Fn(&Value, String) -> Result<String, ShellError>>>,
192) -> Result<Vec<String>, ShellError> {
193    let mut keys = values.to_vec();
194
195    if format.is_none() {
196        keys.sort();
197    }
198
199    Ok(keys)
200}
201
202pub fn sort(planes: &Labels, values: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
203    let tag = tag.into();
204
205    let mut x = vec![];
206    for column in planes.splits() {
207        let key = column.clone().tagged_unknown();
208        let groups = values
209            .get_data_by_key(key.borrow_spanned())
210            .ok_or_else(|| {
211                ShellError::labeled_error("unknown column", "unknown column", key.span())
212            })?;
213
214        let mut y = vec![];
215        for inner_column in planes.grouped() {
216            let key = inner_column.clone().tagged_unknown();
217            let grouped = groups.get_data_by_key(key.borrow_spanned());
218
219            if let Some(grouped) = grouped {
220                y.push(grouped);
221            } else {
222                y.push(UntaggedValue::Table(vec![]).into_value(&tag));
223            }
224        }
225
226        x.push(UntaggedValue::table(&y).into_value(&tag));
227    }
228
229    Ok(UntaggedValue::table(&x).into_value(&tag))
230}
231
232pub fn evaluate(
233    values: &Value,
234    evaluator: &Option<Box<dyn Fn(usize, &Value) -> Result<Value, ShellError> + Send>>,
235    tag: impl Into<Tag>,
236) -> Result<Value, ShellError> {
237    let tag = tag.into();
238
239    let mut x = vec![];
240    for split in values.table_entries() {
241        let mut y = vec![];
242
243        for (idx, subset) in split.table_entries().enumerate() {
244            if let UntaggedValue::Table(values) = &subset.value {
245                if let Some(ref evaluator) = evaluator {
246                    let mut evaluations = vec![];
247
248                    for set in values {
249                        evaluations.push(evaluator(idx, set)?);
250                    }
251
252                    y.push(UntaggedValue::Table(evaluations).into_value(&tag));
253                } else {
254                    y.push(
255                        UntaggedValue::Table(
256                            values
257                                .iter()
258                                .map(|_| UntaggedValue::int(1).into_value(&tag))
259                                .collect::<Vec<_>>(),
260                        )
261                        .into_value(&tag),
262                    );
263                }
264            }
265        }
266
267        x.push(UntaggedValue::table(&y).into_value(&tag));
268    }
269
270    Ok(UntaggedValue::table(&x).into_value(&tag))
271}
272
273pub enum Reduction {
274    Count,
275    Accumulate,
276}
277
278pub fn reduce(
279    values: &Value,
280    reduction_with: &Reduction,
281    tag: impl Into<Tag>,
282) -> Result<Value, ShellError> {
283    let tag = tag.into();
284    let reduce_with = reducer_for(reduction_with);
285
286    let mut datasets = vec![];
287    for dataset in values.table_entries() {
288        let mut acc = UntaggedValue::int(0).into_value(&tag);
289
290        let mut subsets = vec![];
291        for subset in dataset.table_entries() {
292            acc = reduce_with(&acc, subset.table_entries().collect::<Vec<_>>())?;
293            subsets.push(acc.clone());
294        }
295        datasets.push(UntaggedValue::table(&subsets).into_value(&tag));
296    }
297
298    Ok(UntaggedValue::table(&datasets).into_value(&tag))
299}
300
301pub fn percentages(
302    maxima: &Value,
303    values: &Value,
304    tag: impl Into<Tag>,
305) -> Result<Value, ShellError> {
306    let tag = tag.into();
307
308    let mut x = vec![];
309    for split in values.table_entries() {
310        x.push(
311            UntaggedValue::table(
312                &split
313                    .table_entries()
314                    .filter_map(|s| {
315                        let hundred = UntaggedValue::decimal_from_float(100.0, tag.span);
316
317                        match unsafe_compute_values(Operator::Divide, &hundred, maxima) {
318                            Ok(v) => match unsafe_compute_values(Operator::Multiply, s, &v) {
319                                Ok(v) => Some(v.into_untagged_value()),
320                                Err(_) => None,
321                            },
322                            Err(_) => None,
323                        }
324                    })
325                    .collect::<Vec<_>>(),
326            )
327            .into_value(&tag),
328        );
329    }
330
331    Ok(UntaggedValue::table(&x).into_value(&tag))
332}