稳定键合并与读取顺序
策略的具体实现如下。
ts
export function defineQueryAggregation<Input, Item, Context>(
options: QueryAggregationOptions<Input, Item, Context>,
) {
const execute = options.execute.bind(options),
keyFor = options.key?.bind(options),
merge = options.merge?.bind(options);
return Object.freeze({
/**
* Run every input in order.
*
* @param inputs - Query inputs; order determines both read order and result order.
* @param context - Host context forwarded unchanged to `execute`, `key` and `merge`.
* @returns The merged items and their count.
*/
collect(
inputs: readonly Input[],
context: Context,
execution: Partial<import('../structs/common.js').ExecutionContext> = {},
): { total: number; items: Item[] } {
return nativeSubFlowSync(execution, 'aggregation', 'query-aggregation', () => {
const rows: Item[] = [],
merged = new Map<string, Item>();
for (const input of inputs)
for (const item of synchronous(
execute(input, context),
'Query aggregation must be synchronous',
)) {
if (!keyFor) {
rows.push(item);
continue;
}
const key = synchronous(
keyFor!(item, input, context),
'Aggregation keys must be synchronous',
);
merged.set(
key,
merge
? synchronous(
merge(item, merged.get(key), input, context),
'Aggregation merge must be synchronous',
)
: item,
);
}
const items = keyFor ? [...merged.values()] : rows;
return { total: items.length, items };
});
},
});
}
export function mergeByKey<Item, Key>(
items: Iterable<Item>,
key: (item: Item) => Key,
select: (item: Item, previous: Item | undefined) => Item,
): Item[] {
const values = new Map<Key, Item>();
for (const item of items) {
const id = key(item);
values.set(id, select(item, values.get(id)));
}
return [...values.values()];
}