use crate::arena::{ContextStack, DataValue, IterGuard, bvec};
use crate::operators::comparison::compare_equals;
use crate::{CompiledNode, Engine, Result};
use bumpalo::Bump;
use super::input::Items;
#[inline]
pub(crate) fn evaluate_group_by<'a>(
items: Items<'a>,
args: &'a [CompiledNode],
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let Items::Array(src) = items else {
return Err(crate::Error::invalid_args());
};
let len = src.len();
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)?;
guard.stack().charge(groups.len() as u64)?;
let mut matched = false;
for (k, items) in groups.iter_mut() {
if compare_equals(k, key, true, engine, guard.stack())? {
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)))
}