use crate::arena::{ContextStack, DataValue, IterGuard, bvec};
use crate::operators::comparison::compare_equals;
use crate::{CompiledNode, Engine, Result};
use bumpalo::Bump;
use super::helpers::{IterArgKind, ResolvedInput, resolve_iter_input};
#[inline]
pub(crate) fn evaluate_group_by<'a>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
if args.len() < 2 {
return Err(crate::Error::invalid_args());
}
let src = match resolve_iter_input(&args[0], iter_arg_kind, ctx, engine, arena)? {
ResolvedInput::Iterable(s) => s,
ResolvedInput::Empty => return Ok(crate::arena::singletons::singleton_empty_array()),
ResolvedInput::Bridge(av) => {
debug_assert!(!matches!(av, DataValue::Array(_) | DataValue::Null));
return Err(crate::Error::invalid_args());
}
};
let len = src.len();
if len == 0 {
return Ok(crate::arena::singletons::singleton_empty_array());
}
let key_expr = &args[1];
let mut groups = bvec::<(
&'a DataValue<'a>,
bumpalo::collections::Vec<'a, DataValue<'a>>,
)>(arena, len.min(8));
let mut guard = IterGuard::new(ctx);
for i in 0..len {
let item = src.get(i);
guard.step_indexed(item, i);
let key = engine.dispatch_node(key_expr, guard.stack(), arena)?;
let mut matched = false;
for (k, items) in groups.iter_mut() {
if compare_equals(k, key, true, engine)? {
items.push(*item);
matched = true;
break;
}
}
if !matched {
let mut items = bvec::<DataValue<'a>>(arena, 4);
items.push(*item);
groups.push((key, items));
}
}
drop(guard);
let rows = arena.alloc_slice_fill_iter(groups.into_iter().map(|(key, items)| {
let pairs = arena.alloc([
("key", *key),
("items", DataValue::Array(items.into_bump_slice())),
]);
DataValue::Object(&pairs[..])
}));
Ok(arena.alloc(DataValue::Array(rows)))
}