138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
import { type Value, toValue, toNull } from 'reefvm'
|
|
|
|
export const list = {
|
|
slice: (list: any[], start: number, end?: number) => list.slice(start, end),
|
|
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
|
|
},
|
|
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),
|
|
'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
|
|
const realItems = items.map(item => item.value)
|
|
return toValue(realList.splice(realStart, realDeleteCount, ...realItems))
|
|
},
|
|
|
|
// 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<string, any[]> = {}
|
|
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 |