Last active
December 6, 2024 03:30
-
-
Save webstrand/a09b6ec6b041b91f8e9543c2ff45f31a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function ordgroupIterable<reference, key>( | |
refs: Accessor<Iterable<reference>>, | |
keyFn: (ref: reference) => key | |
): Accessor<ReadonlyMap<key, Accessor<readonly reference[]>>> { | |
const signals = createMemo((keyset: Map<key, Signal<readonly reference[]>>) => { | |
const exists = new Set(); | |
let changed = false; | |
// Check for group creations | |
const ord: key[] = []; | |
for(const ref of refs()) { | |
const key = keyFn(ref); | |
exists.add(key); | |
if(!changed) ord.push(key); | |
if(keyset.has(key)) { | |
// group already exists, no work necessary | |
} | |
else { | |
changed = true; | |
keyset.set(key, createSignal<readonly reference[]>([], { equals: arrayEq })); | |
} | |
} | |
// Check for group deletions | |
const oldord: key[] = []; | |
for(const key of keyset.keys()) { | |
if(exists.has(key)) { | |
if(!changed) oldord.push(key); | |
} | |
else { | |
changed = true; | |
keyset.delete(key); | |
} | |
} | |
// Check that the keyset order has not changed | |
if(!changed) { | |
for(let i = 0; i < oldord.length; i++) { | |
if(ord[i] !== oldord[i]) { | |
changed = true; | |
break; | |
} | |
} | |
} | |
// We only create a new reference and trigger an update when the keyset changes. | |
return changed ? new Map(ord.map(key => [key, keyset.get(key)!])) : keyset; | |
}, new Map()); | |
// Impure work of updating the signals | |
createComputed((prevGroups: ReadonlyMap<key, Signal<readonly reference[]>> | undefined) => { | |
const groups = signals(); | |
// Fill new and existing groups with data | |
const work = Map.groupBy(refs(), keyFn); | |
for(const { 0: key, 1: { 1: set }} of groups) { | |
if(!work.has(key)) { | |
console.warn("Attempted to fill an unknown group"); | |
continue; | |
} | |
set(work.get(key)!); | |
} | |
// Clean up any previous groups that no longer exist | |
if(prevGroups) | |
for(const { 0: key, 1: { 1: set } } of prevGroups) { | |
if(groups.has(key)) continue; | |
set([]); | |
} | |
return groups; | |
}) | |
return createMemo(() => new Map(Array.from(signals(), ({ 0: key, 1: { 0: get }}) => [key, get]))); | |
} | |
function arrayEq(a: readonly unknown[], b: readonly unknown[]): boolean { | |
if(a.length !== b.length) return false; | |
for(let i = 0; i < a.length; i++) { | |
if(a[i] !== b[i]) return false; | |
} | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment