import { type Value, toValue, toNull } from 'reefvm' export const list = { slice: (list: any[], start: number, end?: number) => list.slice(start, end ? end : undefined), map: async (list: any[], cb: Function) => { let acc: any[] = [] for (const value of list) acc.push(await cb(value)) return acc }, filter: async (list: any[], cb: Function) => { let acc: any[] = [] for (const value of list) { if (await cb(value)) acc.push(value) } return acc }, reject: async (list: any[], cb: Function) => { let acc: any[] = [] for (const value of list) { if (!(await cb(value))) acc.push(value) } return acc }, reduce: async (list: any[], cb: Function, initial: any) => { let acc = initial for (const value of list) acc = await cb(acc, value) return acc }, find: async (list: any[], cb: Function) => { for (const value of list) { if (await cb(value)) return value } return null }, // predicates 'empty?': (list: any[]) => list.length === 0, 'contains?': (list: any[], item: any) => list.includes(item), 'includes?': (list: any[], item: any) => list.includes(item), 'has?': (list: any[], item: any) => list.includes(item), 'any?': async (list: any[], cb: Function) => { for (const value of list) { if (await cb(value)) return true } return false }, 'all?': async (list: any[], cb: Function) => { for (const value of list) { if (!(await cb(value))) return false } return true }, // mutating push: (list: Value, item: Value) => { if (list.type !== 'array') return toNull() return toValue(list.value.push(item)) }, pop: (list: Value) => { if (list.type !== 'array') return toNull() return toValue(list.value.pop()) }, shift: (list: Value) => { if (list.type !== 'array') return toNull() return toValue(list.value.shift()) }, unshift: (list: Value, item: Value) => { if (list.type !== 'array') return toNull() return toValue(list.value.unshift(item)) }, splice: (list: Value, start: Value, deleteCount: Value, ...items: Value[]) => { const realList = list.value as any[] const realStart = start.value as number const realDeleteCount = deleteCount.value as number return toValue(realList.splice(realStart, realDeleteCount, ...items)) }, insert: (list: Value, index: Value, item: Value) => { if (list.type !== 'array') return toNull() const realList = list.value as any[] const realIndex = index.value as number realList.splice(realIndex, 0, item) return toValue(realList.length) }, // sequence operations reverse: (list: any[]) => list.slice().reverse(), sort: async (list: any[], cb?: (a: any, b: any) => number) => { const arr = [...list] if (!cb) return arr.sort() for (let i = 0; i < arr.length; i++) for (let j = i + 1; j < arr.length; j++) if ((await cb(arr[i], arr[j])) > 0) [arr[i], arr[j]] = [arr[j], arr[i]] return arr }, concat: (...lists: any[][]) => lists.flat(1), flatten: (list: any[], depth: number = 1) => list.flat(depth), unique: (list: any[]) => Array.from(new Set(list)), zip: (list1: any[], list2: any[]) => list1.map((item, i) => [item, list2[i]]), // access first: (list: any[]) => list[0] ?? null, last: (list: any[]) => list[list.length - 1] ?? null, rest: (list: any[]) => list.slice(1), take: (list: any[], n: number) => { if (n < 0) throw new Error(`take: count must be non-negative, got ${n}`) return list.slice(0, n) }, drop: (list: any[], n: number) => { if (n < 0) throw new Error(`drop: count must be non-negative, got ${n}`) return list.slice(n) }, append: (list: any[], item: any) => [...list, item], prepend: (list: any[], item: any) => [item, ...list], 'index-of': (list: any[], item: any) => list.indexOf(item), // utilities sum: (list: any[]) => list.reduce((acc, x) => acc + x, 0), count: async (list: any[], cb: Function) => { let count = 0 for (const value of list) { if (await cb(value)) count++ } return count }, partition: async (list: any[], cb: Function) => { const truthy: any[] = [] const falsy: any[] = [] for (const value of list) { if (await cb(value)) truthy.push(value) else falsy.push(value) } return [truthy, falsy] }, compact: (list: any[]) => list.filter((x) => x != null), 'group-by': async (list: any[], cb: Function) => { const groups: Record = {} for (const value of list) { const key = String(await cb(value)) if (!groups[key]) groups[key] = [] groups[key].push(value) } return groups }, } // raw functions deal directly in Value types, meaning we can modify collection // careful - they MUST return a Value! ;(list.splice as any).raw = true ;(list.push as any).raw = true ;(list.pop as any).raw = true ;(list.shift as any).raw = true ;(list.unshift as any).raw = true ;(list.insert as any).raw = true