shrimp/src/prelude/list.ts

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