diff options
author | (quasar) nebula <qznebula@protonmail.com> | 2024-06-02 22:24:16 -0300 |
---|---|---|
committer | (quasar) nebula <qznebula@protonmail.com> | 2024-06-12 17:26:54 -0300 |
commit | 07571036dd6baa7d854a79380048f64ba8623615 (patch) | |
tree | 6b36f018bfb717c78ee50c9a53b54933f24435a8 /src/data/composite | |
parent | 1ade1832435c0f03c625def19ec8a955c56cb943 (diff) |
data: withReverse{Reference,Contribution}List: factor commonality
This doesn't actually move the common behavior into e.g. devoted component compositions - these two still mirror each other - but it *does* isolate the differing behavior in terms of dependencies that are computed uniquely but surrounded by identical compositional steps. This generally seems like a good fit for compositional subroutines, but those aren't official yet. Meanwhile, this is still factored much better than the previous implementation, and hopefully easier to follow as well!
Diffstat (limited to 'src/data/composite')
-rw-r--r-- | src/data/composite/wiki-data/withReverseContributionList.js | 119 | ||||
-rw-r--r-- | src/data/composite/wiki-data/withReverseReferenceList.js | 111 |
2 files changed, 196 insertions, 34 deletions
diff --git a/src/data/composite/wiki-data/withReverseContributionList.js b/src/data/composite/wiki-data/withReverseContributionList.js index b7f7a95b..63e712bb 100644 --- a/src/data/composite/wiki-data/withReverseContributionList.js +++ b/src/data/composite/wiki-data/withReverseContributionList.js @@ -10,9 +10,11 @@ // is used, a fresh cache will always be created. import {input, templateCompositeFrom} from '#composite'; +import {stitchArrays} from '#sugar'; import {exitWithoutDependency, raiseOutputWithoutDependency} from '#composite/control-flow'; +import {withFlattenedList, withMappedList} from '#composite/data'; import inputWikiData from './inputWikiData.js'; @@ -33,6 +35,8 @@ export default templateCompositeFrom({ outputs: ['#reverseContributionList'], steps: () => [ + // Common behavior -- + // Early exit with an empty array if the data list isn't available. exitWithoutDependency({ dependency: input('data'), @@ -46,45 +50,122 @@ export default templateCompositeFrom({ output: input.value({'#reverseContributionList': []}), }), + // Check for an existing cache record which corresponds to this + // input('list') and input('data'). If it exists, query it for the + // current thing, and raise that; if it doesn't, create it, put it + // where it needs to be, and provide it so the next steps can fill + // it in. { - dependencies: [input.myself(), input('data'), input('list')], + dependencies: [input('list'), input('data'), input.myself()], compute: (continuation, { - [input.myself()]: myself, - [input('data')]: data, [input('list')]: list, + [input('data')]: data, + [input.myself()]: myself, }) => { if (!caches.has(list)) { - caches.set(list, new WeakMap()); + const cache = new WeakMap(); + caches.set(list, cache); + + const cacheRecord = new WeakMap(); + cache.set(data, cacheRecord); + + return continuation({ + ['#cacheRecord']: cacheRecord, + }); } const cache = caches.get(list); if (!cache.has(data)) { const cacheRecord = new WeakMap(); + cache.set(data, cacheRecord); + + return continuation({ + ['#cacheRecord']: cacheRecord, + }); + } - for (const referencingThing of data) { - const contributionList = referencingThing[list]; + return continuation.raiseOutput({ + ['#reverseContributionList']: + cache.get(data).get(myself) ?? [], + }); + }, + }, - for (const contribution of contributionList) { - const {artist} = contribution; + // Unique behavior for contribution lists -- + + { + dependencies: [input('list')], + compute: (continuation, { + [input('list')]: list, + }) => continuation({ + ['#contributionListMap']: + thing => thing[list], + }), + }, - if (cacheRecord.has(artist)) { - cacheRecord.get(artist).push(contribution); + withMappedList({ + list: input('data'), + map: '#contributionListMap', + }).outputs({ + '#mappedList': '#contributionLists', + }), + + withFlattenedList({ + list: '#contributionLists', + }).outputs({ + '#flattenedList': '#referencingThings', + }), + + withMappedList({ + list: '#referencingThings', + map: input.value(contrib => [contrib.artist]), + }).outputs({ + '#mappedList': '#referencedThings', + }), + + // Common behavior -- + + // Actually fill in the cache record. Since we're building up a *reverse* + // reference list, track connections in terms of the referenced thing. + // No newly-provided dependencies here since we're mutating the cache + // record, which is properly in store and will probably be reused in the + // future (and certainly in the next step). + { + dependencies: ['#cacheRecord', '#referencingThings', '#referencedThings'], + compute: (continuation, { + ['#cacheRecord']: cacheRecord, + ['#referencingThings']: referencingThings, + ['#referencedThings']: referencedThings, + }) => { + stitchArrays({ + referencingThing: referencingThings, + referencedThings: referencedThings, + }).forEach(({referencingThing, referencedThings}) => { + for (const referencedThing of referencedThings) { + if (cacheRecord.has(referencedThing)) { + cacheRecord.get(referencedThing).push(referencingThing); } else { - cacheRecord.set(artist, [contribution]); + cacheRecord.set(referencedThing, [referencingThing]); } } - } + }); - cache.set(data, cacheRecord); - } - - return continuation({ - ['#reverseContributionList']: - cache.get(data).get(myself) ?? [], - }); + return continuation(); }, }, + + // Then just pluck out the current object from the now-filled cache record! + { + dependencies: ['#cacheRecord', input.myself()], + compute: (continuation, { + ['#cacheRecord']: cacheRecord, + [input.myself()]: myself, + }) => continuation({ + ['#reverseContributionList']: + cacheRecord.get(myself) ?? [], + }), + }, ], }); diff --git a/src/data/composite/wiki-data/withReverseReferenceList.js b/src/data/composite/wiki-data/withReverseReferenceList.js index 8cd540a5..1f8c082f 100644 --- a/src/data/composite/wiki-data/withReverseReferenceList.js +++ b/src/data/composite/wiki-data/withReverseReferenceList.js @@ -12,9 +12,11 @@ // so any changes should be reflected there (until these are combined). import {input, templateCompositeFrom} from '#composite'; +import {stitchArrays} from '#sugar'; import {exitWithoutDependency, raiseOutputWithoutDependency} from '#composite/control-flow'; +import {withMappedList} from '#composite/data'; import inputWikiData from './inputWikiData.js'; @@ -35,6 +37,8 @@ export default templateCompositeFrom({ outputs: ['#reverseReferenceList'], steps: () => [ + // Common behavior -- + // Early exit with an empty array if the data list isn't available. exitWithoutDependency({ dependency: input('data'), @@ -48,42 +52,119 @@ export default templateCompositeFrom({ output: input.value({'#reverseReferenceList': []}), }), + // Check for an existing cache record which corresponds to this + // input('list') and input('data'). If it exists, query it for the + // current thing, and raise that; if it doesn't, create it, put it + // where it needs to be, and provide it so the next steps can fill + // it in. { - dependencies: [input.myself(), input('data'), input('list')], + dependencies: [input('list'), input('data'), input.myself()], compute: (continuation, { - [input.myself()]: myself, - [input('data')]: data, [input('list')]: list, + [input('data')]: data, + [input.myself()]: myself, }) => { if (!caches.has(list)) { - caches.set(list, new WeakMap()); + const cache = new WeakMap(); + caches.set(list, cache); + + const cacheRecord = new WeakMap(); + cache.set(data, cacheRecord); + + return continuation({ + ['#cacheRecord']: cacheRecord, + }); } const cache = caches.get(list); if (!cache.has(data)) { const cacheRecord = new WeakMap(); + cache.set(data, cacheRecord); + + return continuation({ + ['#cacheRecord']: cacheRecord, + }); + } + + return continuation.raiseOutput({ + ['#reverseReferenceList']: + cache.get(data).get(myself) ?? [], + }); + }, + }, + + // Unique behavior for reference lists -- + + { + dependencies: [input('list')], + compute: (continuation, { + [input('list')]: list, + }) => continuation({ + ['#referenceMap']: + thing => thing[list], + }), + }, + + withMappedList({ + list: input('data'), + map: '#referenceMap', + }).outputs({ + '#mappedList': '#referencedThings', + }), + + { + dependencies: [input('data')], + compute: (continuation, { + [input('data')]: data, + }) => continuation({ + ['#referencingThings']: + data, + }), + }, + + // Common behavior -- - for (const referencingThing of data) { - const referenceList = referencingThing[list]; - for (const referencedThing of referenceList) { + // Actually fill in the cache record. Since we're building up a *reverse* + // reference list, track connections in terms of the referenced thing. + // No newly-provided dependencies here since we're mutating the cache + // record, which is properly in store and will probably be reused in the + // future (and certainly in the next step). + { + dependencies: ['#cacheRecord', '#referencingThings', '#referencedThings'], + compute: (continuation, { + ['#cacheRecord']: cacheRecord, + ['#referencingThings']: referencingThings, + ['#referencedThings']: referencedThings, + }) => { + stitchArrays({ + referencingThing: referencingThings, + referencedThings: referencedThings, + }).forEach(({referencingThing, referencedThings}) => { + for (const referencedThing of referencedThings) { if (cacheRecord.has(referencedThing)) { cacheRecord.get(referencedThing).push(referencingThing); } else { cacheRecord.set(referencedThing, [referencingThing]); } } - } + }); - cache.set(data, cacheRecord); - } - - return continuation({ - ['#reverseReferenceList']: - cache.get(data).get(myself) ?? [], - }); + return continuation(); }, }, + + // Then just pluck out the current object from the now-filled cache record! + { + dependencies: ['#cacheRecord', input.myself()], + compute: (continuation, { + ['#cacheRecord']: cacheRecord, + [input.myself()]: myself, + }) => continuation({ + ['#reverseReferenceList']: + cacheRecord.get(myself) ?? [], + }), + }, ], }); |